Make HTTP (SOAP) sockets non blocking

This commit is contained in:
Thomas Bernard 2012-02-07 01:26:15 +01:00
parent 0d0a50a084
commit af60fee984
5 changed files with 91 additions and 83 deletions

View File

@ -1,4 +1,7 @@
$Id: Changelog.txt,v 1.255 2012/02/06 16:32:07 nanard Exp $ $Id: Changelog.txt,v 1.256 2012/02/07 00:21:51 nanard Exp $
2012/02/06:
Make HTTP (SOAP) sockets non blocking.
2012/02/05: 2012/02/05:
Compile ok with -ansi flag. Compile ok with -ansi flag.

View File

@ -1,4 +1,4 @@
/* $Id: miniupnpd.c,v 1.142 2012/02/04 23:34:39 nanard Exp $ */ /* $Id: miniupnpd.c,v 1.143 2012/02/07 00:21:52 nanard Exp $ */
/* MiniUPnP project /* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2012 Thomas Bernard * (c) 2006-2012 Thomas Bernard
@ -1091,9 +1091,7 @@ main(int argc, char * * argv)
struct upnphttp * e = 0; struct upnphttp * e = 0;
struct upnphttp * next; struct upnphttp * next;
fd_set readset; /* for select() */ fd_set readset; /* for select() */
#ifdef ENABLE_EVENTS
fd_set writeset; fd_set writeset;
#endif
struct timeval timeout, timeofday, lasttimeofday = {0, 0}; struct timeval timeout, timeofday, lasttimeofday = {0, 0};
int max_fd = -1; int max_fd = -1;
#ifdef USE_MINIUPNPDCTL #ifdef USE_MINIUPNPDCTL
@ -1359,6 +1357,7 @@ main(int argc, char * * argv)
/* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */ /* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */
FD_ZERO(&readset); FD_ZERO(&readset);
FD_ZERO(&writeset);
if (sudp >= 0) if (sudp >= 0)
{ {
@ -1396,9 +1395,14 @@ main(int argc, char * * argv)
i = 0; /* active HTTP connections count */ i = 0; /* active HTTP connections count */
for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next) for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next)
{ {
if((e->socket >= 0) && (e->state <= 2)) if(e->socket >= 0)
{ {
if(e->state <= EWaitingForHttpContent)
FD_SET(e->socket, &readset); FD_SET(e->socket, &readset);
else if(e->state == ESendingAndClosing)
FD_SET(e->socket, &writeset);
else
continue;
max_fd = MAX(max_fd, e->socket); max_fd = MAX(max_fd, e->socket);
i++; i++;
} }
@ -1434,15 +1438,10 @@ main(int argc, char * * argv)
#endif #endif
#ifdef ENABLE_EVENTS #ifdef ENABLE_EVENTS
FD_ZERO(&writeset);
upnpevents_selectfds(&readset, &writeset, &max_fd); upnpevents_selectfds(&readset, &writeset, &max_fd);
#endif #endif
#ifdef ENABLE_EVENTS
if(select(max_fd+1, &readset, &writeset, 0, &timeout) < 0) if(select(max_fd+1, &readset, &writeset, 0, &timeout) < 0)
#else
if(select(max_fd+1, &readset, 0, 0, &timeout) < 0)
#endif
{ {
if(quitting) goto shutdown; if(quitting) goto shutdown;
if(errno == EINTR) continue; /* interrupted by a signal, start again */ if(errno == EINTR) continue; /* interrupted by a signal, start again */
@ -1539,12 +1538,15 @@ main(int argc, char * * argv)
/* LIST_FOREACH macro is not available under linux */ /* LIST_FOREACH macro is not available under linux */
for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next) for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next)
{ {
if( (e->socket >= 0) && (e->state <= 2) if(e->socket >= 0)
&&(FD_ISSET(e->socket, &readset)) ) {
if(FD_ISSET(e->socket, &readset) ||
FD_ISSET(e->socket, &writeset))
{ {
Process_upnphttp(e); Process_upnphttp(e);
} }
} }
}
/* process incoming HTTP connections */ /* process incoming HTTP connections */
if(shttpl >= 0 && FD_ISSET(shttpl, &readset)) if(shttpl >= 0 && FD_ISSET(shttpl, &readset))
{ {
@ -1623,7 +1625,7 @@ main(int argc, char * * argv)
for(e = upnphttphead.lh_first; e != NULL; ) for(e = upnphttphead.lh_first; e != NULL; )
{ {
next = e->entries.le_next; next = e->entries.le_next;
if(e->state >= 100) if(e->state >= EToDelete)
{ {
LIST_REMOVE(e, entries); LIST_REMOVE(e, entries);
Delete_upnphttp(e); Delete_upnphttp(e);

View File

@ -1,4 +1,4 @@
/* $Id: upnpevents.c,v 1.19 2012/02/06 16:21:24 nanard Exp $ */ /* $Id: upnpevents.c,v 1.20 2012/02/06 23:41:15 nanard Exp $ */
/* MiniUPnP project /* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2008-2012 Thomas Bernard * (c) 2008-2012 Thomas Bernard
@ -368,11 +368,16 @@ static void upnp_event_send(struct upnp_event_notify * obj)
"upnp_event_send", obj->buffer + obj->sent); "upnp_event_send", obj->buffer + obj->sent);
i = send(obj->s, obj->buffer + obj->sent, obj->tosend - obj->sent, 0); i = send(obj->s, obj->buffer + obj->sent, obj->tosend - obj->sent, 0);
if(i<0) { if(i<0) {
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
syslog(LOG_NOTICE, "%s: send(): %m", "upnp_event_send"); syslog(LOG_NOTICE, "%s: send(): %m", "upnp_event_send");
obj->state = EError; obj->state = EError;
return; return;
} else {
/* EAGAIN or EWOULDBLOCK or EINTR : no data sent */
i = 0;
} }
else if(i != (obj->tosend - obj->sent)) }
if(i != (obj->tosend - obj->sent))
syslog(LOG_NOTICE, "%s: %d bytes send out of %d", syslog(LOG_NOTICE, "%s: %d bytes send out of %d",
"upnp_event_send", i, obj->tosend - obj->sent); "upnp_event_send", i, obj->tosend - obj->sent);
obj->sent += i; obj->sent += i;

View File

@ -1,4 +1,4 @@
/* $Id: upnphttp.c,v 1.66 2012/02/01 11:13:30 nanard Exp $ */ /* $Id: upnphttp.c,v 1.67 2012/02/07 00:21:54 nanard Exp $ */
/* Project : miniupnp /* Project : miniupnp
* Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author : Thomas Bernard * Author : Thomas Bernard
@ -24,6 +24,7 @@
#include "miniupnpdpath.h" #include "miniupnpdpath.h"
#include "upnpsoap.h" #include "upnpsoap.h"
#include "upnpevents.h" #include "upnpevents.h"
#include "upnputils.h"
struct upnphttp * struct upnphttp *
New_upnphttp(int s) New_upnphttp(int s)
@ -36,6 +37,8 @@ New_upnphttp(int s)
return NULL; return NULL;
memset(ret, 0, sizeof(struct upnphttp)); memset(ret, 0, sizeof(struct upnphttp));
ret->socket = s; ret->socket = s;
if(!set_non_blocking(s))
syslog(LOG_WARNING, "New_upnphttp::set_non_blocking(): %m");
return ret; return ret;
} }
@ -47,7 +50,7 @@ CloseSocket_upnphttp(struct upnphttp * h)
syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket); syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket);
} }
h->socket = -1; h->socket = -1;
h->state = 100; h->state = EToDelete;
} }
void void
@ -159,24 +162,11 @@ intervening space) by either an integer or the keyword "infinite". */
static void static void
Send404(struct upnphttp * h) Send404(struct upnphttp * h)
{ {
/*
static const char error404[] = "HTTP/1.1 404 Not found\r\n"
"Connection: close\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
"<BODY><H1>Not Found</H1>The requested URL was not found"
" on this server.</BODY></HTML>\r\n";
int n;
n = send(h->socket, error404, sizeof(error404) - 1, 0);
if(n < 0)
{
syslog(LOG_ERR, "Send404: send(http): %m");
}*/
static const char body404[] = static const char body404[] =
"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>" "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
"<BODY><H1>Not Found</H1>The requested URL was not found" "<BODY><H1>Not Found</H1>The requested URL was not found"
" on this server.</BODY></HTML>\r\n"; " on this server.</BODY></HTML>\r\n";
h->respflags = FLAG_HTML; h->respflags = FLAG_HTML;
BuildResp2_upnphttp(h, 404, "Not Found", BuildResp2_upnphttp(h, 404, "Not Found",
body404, sizeof(body404) - 1); body404, sizeof(body404) - 1);
@ -187,25 +177,11 @@ Send404(struct upnphttp * h)
static void static void
Send501(struct upnphttp * h) Send501(struct upnphttp * h)
{ {
/*
static const char error501[] = "HTTP/1.1 501 Not Implemented\r\n"
"Connection: close\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
"<BODY><H1>Not Implemented</H1>The HTTP Method "
"is not implemented by this server.</BODY></HTML>\r\n";
int n;
n = send(h->socket, error501, sizeof(error501) - 1, 0);
if(n < 0)
{
syslog(LOG_ERR, "Send501: send(http): %m");
}
*/
static const char body501[] = static const char body501[] =
"<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>" "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
"<BODY><H1>Not Implemented</H1>The HTTP Method " "<BODY><H1>Not Implemented</H1>The HTTP Method "
"is not implemented by this server.</BODY></HTML>\r\n"; "is not implemented by this server.</BODY></HTML>\r\n";
h->respflags = FLAG_HTML; h->respflags = FLAG_HTML;
BuildResp2_upnphttp(h, 501, "Not Implemented", BuildResp2_upnphttp(h, 501, "Not Implemented",
body501, sizeof(body501) - 1); body501, sizeof(body501) - 1);
@ -296,7 +272,7 @@ ProcessHTTPPOST_upnphttp(struct upnphttp * h)
else else
{ {
/* waiting for remaining data */ /* waiting for remaining data */
h->state = 1; h->state = EWaitingForHttpContent;
} }
} }
@ -546,12 +522,13 @@ Process_upnphttp(struct upnphttp * h)
{ {
char buf[2048]; char buf[2048];
int n; int n;
if(!h) if(!h)
return; return;
switch(h->state) switch(h->state)
{ {
case 0: case EWaitingForHttpRequest:
n = recv(h->socket, buf, 2048, 0); n = recv(h->socket, buf, sizeof(buf), 0);
if(n<0) if(n<0)
{ {
if(errno != EAGAIN && if(errno != EAGAIN &&
@ -559,14 +536,14 @@ Process_upnphttp(struct upnphttp * h)
errno != EINTR) errno != EINTR)
{ {
syslog(LOG_ERR, "recv (state0): %m"); syslog(LOG_ERR, "recv (state0): %m");
h->state = 100; h->state = EToDelete;
} }
/* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */ /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
} }
else if(n==0) else if(n==0)
{ {
syslog(LOG_WARNING, "HTTP Connection closed unexpectedly"); syslog(LOG_WARNING, "HTTP Connection closed unexpectedly");
h->state = 100; h->state = EToDelete;
} }
else else
{ {
@ -586,8 +563,8 @@ Process_upnphttp(struct upnphttp * h)
} }
} }
break; break;
case 1: case EWaitingForHttpContent:
n = recv(h->socket, buf, 2048, 0); n = recv(h->socket, buf, sizeof(buf), 0);
if(n<0) if(n<0)
{ {
if(errno != EAGAIN && if(errno != EAGAIN &&
@ -595,19 +572,26 @@ Process_upnphttp(struct upnphttp * h)
errno != EINTR) errno != EINTR)
{ {
syslog(LOG_ERR, "recv (state1): %m"); syslog(LOG_ERR, "recv (state1): %m");
h->state = 100; h->state = EToDelete;
} }
/* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */ /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
} }
else if(n==0) else if(n==0)
{ {
syslog(LOG_WARNING, "HTTP Connection closed inexpectedly"); syslog(LOG_WARNING, "HTTP Connection closed inexpectedly");
h->state = 100; h->state = EToDelete;
} }
else else
{ {
/*fwrite(buf, 1, n, stdout);*/ /* debug */ void * tmp = realloc(h->req_buf, n + h->req_buflen);
h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen); if(!tmp)
{
syslog(LOG_ERR, "memory allocation error %m");
h->state = EToDelete;
}
else
{
h->req_buf = tmp;
memcpy(h->req_buf + h->req_buflen, buf, n); memcpy(h->req_buf + h->req_buflen, buf, n);
h->req_buflen += n; h->req_buflen += n;
if((h->req_buflen - h->req_contentoff) >= h->req_contentlen) if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
@ -615,6 +599,10 @@ Process_upnphttp(struct upnphttp * h)
ProcessHTTPPOST_upnphttp(h); ProcessHTTPPOST_upnphttp(h);
} }
} }
}
break;
case ESendingAndClosing:
SendRespAndClose_upnphttp(h);
break; break;
default: default:
syslog(LOG_WARNING, "Unexpected state: %d", h->state); syslog(LOG_WARNING, "Unexpected state: %d", h->state);
@ -725,30 +713,34 @@ BuildResp_upnphttp(struct upnphttp * h,
void void
SendRespAndClose_upnphttp(struct upnphttp * h) SendRespAndClose_upnphttp(struct upnphttp * h)
{ {
char * p;
ssize_t n; ssize_t n;
size_t len;
p = h->res_buf; while (h->res_sent < h->res_buflen)
len = h->res_buflen;
while (len > 0)
{ {
n = send(h->socket, p, len, 0); n = send(h->socket, h->res_buf + h->res_sent,
h->res_buflen - h->res_sent, 0);
if(n<0) if(n<0)
{ {
if(errno == EINTR)
continue; /* try again immediatly */
if(errno == EAGAIN || errno == EWOULDBLOCK)
{
/* try again later */
h->state = ESendingAndClosing;
return;
}
syslog(LOG_ERR, "send(res_buf): %m"); syslog(LOG_ERR, "send(res_buf): %m");
if (errno != EINTR)
break; /* avoid infinite loop */ break; /* avoid infinite loop */
} }
else if(n == 0) else if(n == 0)
{ {
syslog(LOG_ERR, "send(res_buf): %zd bytes sent (out of %zu)", syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
n, len); h->res_sent, h->res_buflen);
break; break;
} }
else else
{ {
p += n; h->res_sent += n;
len -= n;
} }
} }
CloseSocket_upnphttp(h); CloseSocket_upnphttp(h);

View File

@ -1,4 +1,4 @@
/* $Id: upnphttp.h,v 1.25 2011/11/18 11:21:18 nanard Exp $ */ /* $Id: upnphttp.h,v 1.26 2012/02/07 00:21:54 nanard Exp $ */
/* MiniUPnP project /* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard * (c) 2006-2011 Thomas Bernard
@ -23,6 +23,13 @@
... ...
>= 100 - to be deleted >= 100 - to be deleted
*/ */
enum httpStates {
EWaitingForHttpRequest = 0,
EWaitingForHttpContent,
ESendingAndClosing,
EToDelete = 100
};
enum httpCommands { enum httpCommands {
EUnknown = 0, EUnknown = 0,
EGet, EGet,
@ -38,7 +45,7 @@ struct upnphttp {
int ipv6; int ipv6;
struct in6_addr clientaddr_v6; struct in6_addr clientaddr_v6;
#endif #endif
int state; enum httpStates state;
char HttpVer[16]; char HttpVer[16];
/* request */ /* request */
char * req_buf; char * req_buf;
@ -59,9 +66,8 @@ struct upnphttp {
/* response */ /* response */
char * res_buf; char * res_buf;
int res_buflen; int res_buflen;
int res_sent;
int res_buf_alloclen; int res_buf_alloclen;
/*int res_contentlen;*/
/*int res_contentoff;*/ /* header length */
LIST_ENTRY(upnphttp) entries; LIST_ENTRY(upnphttp) entries;
}; };