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:
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
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2012 Thomas Bernard
@ -1091,9 +1091,7 @@ main(int argc, char * * argv)
struct upnphttp * e = 0;
struct upnphttp * next;
fd_set readset; /* for select() */
#ifdef ENABLE_EVENTS
fd_set writeset;
#endif
struct timeval timeout, timeofday, lasttimeofday = {0, 0};
int max_fd = -1;
#ifdef USE_MINIUPNPDCTL
@ -1359,6 +1357,7 @@ main(int argc, char * * argv)
/* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */
FD_ZERO(&readset);
FD_ZERO(&writeset);
if (sudp >= 0)
{
@ -1396,10 +1395,15 @@ main(int argc, char * * argv)
i = 0; /* active HTTP connections count */
for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next)
{
if((e->socket >= 0) && (e->state <= 2))
if(e->socket >= 0)
{
FD_SET(e->socket, &readset);
max_fd = MAX( max_fd, e->socket);
if(e->state <= EWaitingForHttpContent)
FD_SET(e->socket, &readset);
else if(e->state == ESendingAndClosing)
FD_SET(e->socket, &writeset);
else
continue;
max_fd = MAX(max_fd, e->socket);
i++;
}
}
@ -1434,15 +1438,10 @@ main(int argc, char * * argv)
#endif
#ifdef ENABLE_EVENTS
FD_ZERO(&writeset);
upnpevents_selectfds(&readset, &writeset, &max_fd);
#endif
#ifdef ENABLE_EVENTS
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(errno == EINTR) continue; /* interrupted by a signal, start again */
@ -1539,10 +1538,13 @@ main(int argc, char * * argv)
/* LIST_FOREACH macro is not available under linux */
for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next)
{
if( (e->socket >= 0) && (e->state <= 2)
&&(FD_ISSET(e->socket, &readset)) )
if(e->socket >= 0)
{
Process_upnphttp(e);
if(FD_ISSET(e->socket, &readset) ||
FD_ISSET(e->socket, &writeset))
{
Process_upnphttp(e);
}
}
}
/* process incoming HTTP connections */
@ -1623,7 +1625,7 @@ main(int argc, char * * argv)
for(e = upnphttphead.lh_first; e != NULL; )
{
next = e->entries.le_next;
if(e->state >= 100)
if(e->state >= EToDelete)
{
LIST_REMOVE(e, entries);
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
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (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);
i = send(obj->s, obj->buffer + obj->sent, obj->tosend - obj->sent, 0);
if(i<0) {
syslog(LOG_NOTICE, "%s: send(): %m", "upnp_event_send");
obj->state = EError;
return;
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
syslog(LOG_NOTICE, "%s: send(): %m", "upnp_event_send");
obj->state = EError;
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",
"upnp_event_send", i, obj->tosend - obj->sent);
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
* Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
@ -24,6 +24,7 @@
#include "miniupnpdpath.h"
#include "upnpsoap.h"
#include "upnpevents.h"
#include "upnputils.h"
struct upnphttp *
New_upnphttp(int s)
@ -36,6 +37,8 @@ New_upnphttp(int s)
return NULL;
memset(ret, 0, sizeof(struct upnphttp));
ret->socket = s;
if(!set_non_blocking(s))
syslog(LOG_WARNING, "New_upnphttp::set_non_blocking(): %m");
return ret;
}
@ -47,7 +50,7 @@ CloseSocket_upnphttp(struct upnphttp * h)
syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket);
}
h->socket = -1;
h->state = 100;
h->state = EToDelete;
}
void
@ -159,24 +162,11 @@ intervening space) by either an integer or the keyword "infinite". */
static void
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[] =
"<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";
h->respflags = FLAG_HTML;
BuildResp2_upnphttp(h, 404, "Not Found",
body404, sizeof(body404) - 1);
@ -187,25 +177,11 @@ Send404(struct upnphttp * h)
static void
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[] =
"<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";
h->respflags = FLAG_HTML;
BuildResp2_upnphttp(h, 501, "Not Implemented",
body501, sizeof(body501) - 1);
@ -296,7 +272,7 @@ ProcessHTTPPOST_upnphttp(struct upnphttp * h)
else
{
/* waiting for remaining data */
h->state = 1;
h->state = EWaitingForHttpContent;
}
}
@ -546,12 +522,13 @@ Process_upnphttp(struct upnphttp * h)
{
char buf[2048];
int n;
if(!h)
return;
switch(h->state)
{
case 0:
n = recv(h->socket, buf, 2048, 0);
case EWaitingForHttpRequest:
n = recv(h->socket, buf, sizeof(buf), 0);
if(n<0)
{
if(errno != EAGAIN &&
@ -559,14 +536,14 @@ Process_upnphttp(struct upnphttp * h)
errno != EINTR)
{
syslog(LOG_ERR, "recv (state0): %m");
h->state = 100;
h->state = EToDelete;
}
/* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
}
else if(n==0)
{
syslog(LOG_WARNING, "HTTP Connection closed unexpectedly");
h->state = 100;
h->state = EToDelete;
}
else
{
@ -586,8 +563,8 @@ Process_upnphttp(struct upnphttp * h)
}
}
break;
case 1:
n = recv(h->socket, buf, 2048, 0);
case EWaitingForHttpContent:
n = recv(h->socket, buf, sizeof(buf), 0);
if(n<0)
{
if(errno != EAGAIN &&
@ -595,27 +572,38 @@ Process_upnphttp(struct upnphttp * h)
errno != EINTR)
{
syslog(LOG_ERR, "recv (state1): %m");
h->state = 100;
h->state = EToDelete;
}
/* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
}
else if(n==0)
{
syslog(LOG_WARNING, "HTTP Connection closed inexpectedly");
h->state = 100;
h->state = EToDelete;
}
else
{
/*fwrite(buf, 1, n, stdout);*/ /* debug */
h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen);
memcpy(h->req_buf + h->req_buflen, buf, n);
h->req_buflen += n;
if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
void * tmp = realloc(h->req_buf, n + h->req_buflen);
if(!tmp)
{
ProcessHTTPPOST_upnphttp(h);
syslog(LOG_ERR, "memory allocation error %m");
h->state = EToDelete;
}
else
{
h->req_buf = tmp;
memcpy(h->req_buf + h->req_buflen, buf, n);
h->req_buflen += n;
if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
{
ProcessHTTPPOST_upnphttp(h);
}
}
}
break;
case ESendingAndClosing:
SendRespAndClose_upnphttp(h);
break;
default:
syslog(LOG_WARNING, "Unexpected state: %d", h->state);
}
@ -725,30 +713,34 @@ BuildResp_upnphttp(struct upnphttp * h,
void
SendRespAndClose_upnphttp(struct upnphttp * h)
{
char * p;
ssize_t n;
size_t len;
p = h->res_buf;
len = h->res_buflen;
while (len > 0)
while (h->res_sent < h->res_buflen)
{
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(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");
if (errno != EINTR)
break; /* avoid infinite loop */
break; /* avoid infinite loop */
}
else if(n == 0)
{
syslog(LOG_ERR, "send(res_buf): %zd bytes sent (out of %zu)",
n, len);
syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
h->res_sent, h->res_buflen);
break;
}
else
{
p += n;
len -= n;
h->res_sent += n;
}
}
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
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard
@ -23,6 +23,13 @@
...
>= 100 - to be deleted
*/
enum httpStates {
EWaitingForHttpRequest = 0,
EWaitingForHttpContent,
ESendingAndClosing,
EToDelete = 100
};
enum httpCommands {
EUnknown = 0,
EGet,
@ -38,7 +45,7 @@ struct upnphttp {
int ipv6;
struct in6_addr clientaddr_v6;
#endif
int state;
enum httpStates state;
char HttpVer[16];
/* request */
char * req_buf;
@ -59,9 +66,8 @@ struct upnphttp {
/* response */
char * res_buf;
int res_buflen;
int res_sent;
int res_buf_alloclen;
/*int res_contentlen;*/
/*int res_contentoff;*/ /* header length */
LIST_ENTRY(upnphttp) entries;
};