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
>>
>>
>>
>>
>>
>>
>
>

Attachment: 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, &copyBuffer);
  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 */
}

Reply via email to