Hi, Attached is a small patch to provide 'Connection: Upgrade' capability, with an attempt to do a test with curl. The test is not fully functional since there are some issues with the use of curl: * if the HTTP response code is '101' curl_perform will fail. I didn't look at curl source to understand why... * the test attempt to do an upgrade to PTTH ( http://tools.ietf.org/id/draft-lentczner-rhttp-00.txt), but reverting the use of a FD in a same process, is not really easy. You end up reading the FD you just wrote at the same end of the connection and no data are read.
Anyway, I have done another sample test and the tcpdump capture looks OK. Regards, Bertrand 2012/8/8 Christian Grothoff <[email protected]> > Short answer: nothing has happened since. IIRC the proposed API still had > some issues (but I don't recall which ones right now), and nothing has been > implemented by me (and I have received no patches or specific feedback on > the API). It's still on my agenda, but I'm currently focused on other > issues... > > Happy hacking, > > Christian > > > On 08/08/2012 05:29 PM, Bertrand Baudet wrote: > >> Hello, >> >> I found a thread regarding this subject on Mon Jan 23, 2012: 'Websockets'. >> At this time an API for the upgrade mechanisms was proposed and it seems >> present in SVN, but '#if 0'. >> >> I'm interesting in using a protocol switch (not for websocket) and I >> would like to know if more have been done on this subject? >> >> Regards, >> Bertrand >> >> >> >> >> >> > >
mhd_upgrade.patch
Description: Binary data
/*
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 <curl/curl.h>
#include <microhttpd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifndef WINDOWS
#include <unistd.h>
#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: <XXXX>
*
* @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 */
}
