2020-05-05 15:12:08 +00:00
# include "mio-http.h"
# include "mio-htrd.h"
2020-05-12 11:46:00 +00:00
# include "mio-pro.h" /* for cgi */
2020-05-16 19:12:10 +00:00
# include "mio-fmt.h"
2020-05-20 14:53:05 +00:00
# include "mio-chr.h"
2020-05-20 16:14:36 +00:00
# include "mio-path.h"
2020-05-05 15:12:08 +00:00
# include "mio-prv.h"
2020-05-07 15:47:33 +00:00
# include <unistd.h> /* TODO: move file operations to sys-file.XXX */
# include <fcntl.h>
2020-05-08 09:48:26 +00:00
# include <sys/stat.h>
2020-05-10 16:20:39 +00:00
# include <pthread.h>
2020-05-20 14:53:05 +00:00
# include <stdlib.h> /* setenv, clearenv */
2020-05-07 15:47:33 +00:00
2020-05-18 08:59:25 +00:00
# define CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH
2020-05-12 11:46:00 +00:00
typedef struct mio_svc_htts_cli_t mio_svc_htts_cli_t ;
struct mio_svc_htts_cli_t
{
2020-05-18 16:40:00 +00:00
mio_svc_htts_cli_t * cli_prev ;
mio_svc_htts_cli_t * cli_next ;
2020-05-12 17:53:19 +00:00
/* a listener socket sets htts and sck fields only */
/* a client sockets uses all the fields in this struct */
2020-05-12 11:46:00 +00:00
mio_svc_htts_t * htts ;
mio_dev_sck_t * sck ;
mio_htrd_t * htrd ;
mio_becs_t * sbuf ; /* temporary buffer for status line formatting */
2020-05-12 17:53:19 +00:00
2020-05-18 16:40:00 +00:00
mio_svc_htts_rsrc_t * rsrc ;
2020-05-12 11:46:00 +00:00
} ;
2020-05-05 15:12:08 +00:00
struct mio_svc_htts_t
{
MIO_SVC_HEADER ;
2020-05-20 16:14:36 +00:00
mio_svc_htts_proc_req_t proc_req ;
2020-05-05 15:12:08 +00:00
mio_dev_sck_t * lsck ;
2020-05-12 11:46:00 +00:00
mio_svc_htts_cli_t cli ; /* list head for client list */
2020-05-07 10:10:33 +00:00
mio_bch_t * server_name ;
mio_bch_t server_name_buf [ 64 ] ;
2020-05-05 15:12:08 +00:00
} ;
struct mio_svc_httc_t
{
MIO_SVC_HEADER ;
} ;
2020-05-12 11:46:00 +00:00
/* ------------------------------------------------------------------------- */
2020-05-06 09:28:36 +00:00
2020-05-12 11:46:00 +00:00
# define CLIL_APPEND_CLI(lh,cli) do { \
( cli ) - > cli_next = ( lh ) ; \
( cli ) - > cli_prev = ( lh ) - > cli_prev ; \
( cli ) - > cli_prev - > cli_next = ( cli ) ; \
( lh ) - > cli_prev = ( cli ) ; \
} while ( 0 )
# define CLIL_UNLINK_CLI(cli) do { \
( cli ) - > cli_prev - > cli_next = ( cli ) - > cli_next ; \
( cli ) - > cli_next - > cli_prev = ( cli ) - > cli_prev ; \
} while ( 0 )
2020-05-12 17:53:19 +00:00
# define CLIL_UNLINK_CLI_CLEAN(cli) do { \
( cli ) - > cli_prev - > cli_next = ( cli ) - > cli_next ; \
( cli ) - > cli_next - > cli_prev = ( cli ) - > cli_prev ; \
( cli ) - > cli_prev = ( cli ) ; \
( cli ) - > cli_next = ( cli ) ; \
} while ( 0 )
2020-05-12 11:46:00 +00:00
# define CLIL_INIT(lh) ((lh)->cli_next = (lh)->cli_prev = lh)
# define CLIL_FIRST_CLI(lh) ((lh)->cli_next)
# define CLIL_LAST_CLI(lh) ((lh)->cli_prev)
# define CLIL_IS_EMPTY(lh) (CLIL_FIRST_CLI(lh) == (lh))
# define CLIL_IS_NIL_CLI(lh,cli) ((cli) == (lh))
2020-05-06 09:28:36 +00:00
2020-05-12 11:46:00 +00:00
/* ------------------------------------------------------------------------- */
2020-05-05 15:12:08 +00:00
struct htrd_xtn_t
{
mio_dev_sck_t * sck ;
} ;
typedef struct htrd_xtn_t htrd_xtn_t ;
/* ------------------------------------------------------------------------ */
2020-05-10 16:20:39 +00:00
static int test_func_handler ( int rfd , int wfd )
{
int i ;
/* you can read the post data from rfd;
* you can write result to wfd */
write ( wfd , " Content-Type: text/plain \r \n \r \n " , 28 ) ;
for ( i = 0 ; i < 10 ; i + + )
{
write ( wfd , " hello \n " , 6 ) ;
sleep ( 1 ) ;
}
return - 1 ;
}
2020-05-06 09:28:36 +00:00
static int client_htrd_peek_request ( mio_htrd_t * htrd , mio_htre_t * req )
{
htrd_xtn_t * htrdxtn = ( htrd_xtn_t * ) mio_htrd_getxtn ( htrd ) ;
2020-05-12 11:46:00 +00:00
mio_svc_htts_cli_t * sckxtn = ( mio_svc_htts_cli_t * ) mio_dev_sck_getxtn ( htrdxtn - > sck ) ;
2020-05-20 16:14:36 +00:00
return sckxtn - > htts - > proc_req ( sckxtn - > htts , htrdxtn - > sck , req ) ;
2020-05-06 09:28:36 +00:00
}
static mio_htrd_recbs_t client_htrd_recbs =
{
client_htrd_peek_request ,
2020-05-18 08:59:25 +00:00
MIO_NULL ,
MIO_NULL
2020-05-06 09:28:36 +00:00
} ;
2020-05-12 11:46:00 +00:00
static int init_client ( mio_svc_htts_cli_t * cli , mio_dev_sck_t * sck )
2020-05-06 09:28:36 +00:00
{
htrd_xtn_t * htrdxtn ;
2020-05-12 11:46:00 +00:00
/* the htts field must be filled with the same field in the listening socket upon accept() */
MIO_ASSERT ( sck - > mio , cli - > htts ! = MIO_NULL ) ;
2020-05-12 17:53:19 +00:00
MIO_ASSERT ( sck - > mio , cli - > sck = = cli - > htts - > lsck ) ; /* the field should still point to the listner socket */
2020-05-12 11:46:00 +00:00
MIO_ASSERT ( sck - > mio , sck - > mio = = cli - > htts - > mio ) ;
2020-05-06 09:28:36 +00:00
2020-05-12 11:46:00 +00:00
cli - > htrd = mio_htrd_open ( sck - > mio , MIO_SIZEOF ( * htrdxtn ) ) ;
if ( MIO_UNLIKELY ( ! cli - > htrd ) ) goto oops ;
2020-05-06 09:28:36 +00:00
2020-05-19 09:11:39 +00:00
/* With MIO_HTRD_TRAILERS, htrd stores trailers in a separate place.
* Otherwise , it is merged to the headers . */
/*mio_htrd_setopt (cli->htrd, MIO_HTRD_REQUEST | MIO_HTRD_TRAILERS);*/
2020-05-12 11:46:00 +00:00
cli - > sbuf = mio_becs_open ( sck - > mio , 0 , 2048 ) ;
if ( MIO_UNLIKELY ( ! cli - > sbuf ) ) goto oops ;
2020-05-16 19:12:10 +00:00
2020-05-12 11:46:00 +00:00
htrdxtn = mio_htrd_getxtn ( cli - > htrd ) ;
htrdxtn - > sck = sck ; /* TODO: remember cli instead? */
2020-05-06 09:28:36 +00:00
2020-05-12 11:46:00 +00:00
mio_htrd_setrecbs ( cli - > htrd , & client_htrd_recbs ) ;
cli - > sck = sck ;
CLIL_APPEND_CLI ( & cli - > htts - > cli , cli ) ;
2020-05-12 17:53:19 +00:00
MIO_DEBUG3 ( sck - > mio , " HTTS(%p) - initialized client %p socket %p \n " , cli - > htts , cli , sck ) ;
2020-05-06 09:28:36 +00:00
return 0 ;
oops :
2020-05-12 11:46:00 +00:00
if ( cli - > sbuf )
2020-05-06 09:28:36 +00:00
{
2020-05-12 11:46:00 +00:00
mio_becs_close ( cli - > sbuf ) ;
cli - > sbuf = MIO_NULL ;
2020-05-06 09:28:36 +00:00
}
2020-05-12 11:46:00 +00:00
if ( cli - > htrd )
2020-05-06 09:28:36 +00:00
{
2020-05-12 11:46:00 +00:00
mio_htrd_close ( cli - > htrd ) ;
cli - > htrd = MIO_NULL ;
2020-05-06 09:28:36 +00:00
}
return - 1 ;
}
2020-05-12 11:46:00 +00:00
static void fini_client ( mio_svc_htts_cli_t * cli )
2020-05-06 09:28:36 +00:00
{
2020-05-12 17:53:19 +00:00
MIO_DEBUG3 ( cli - > sck - > mio , " HTTS(%p) - finalizing client %p socket %p \n " , cli - > htts , cli , cli - > sck ) ;
2020-05-18 16:40:00 +00:00
if ( cli - > rsrc )
{
mio_svc_htts_rsrc_kill ( cli - > rsrc ) ;
cli - > rsrc = MIO_NULL ;
}
2020-05-12 11:46:00 +00:00
if ( cli - > sbuf )
2020-05-06 09:28:36 +00:00
{
2020-05-12 11:46:00 +00:00
mio_becs_close ( cli - > sbuf ) ;
cli - > sbuf = MIO_NULL ;
2020-05-06 09:28:36 +00:00
}
2020-05-18 16:40:00 +00:00
2020-05-12 11:46:00 +00:00
if ( cli - > htrd )
2020-05-06 09:28:36 +00:00
{
2020-05-12 11:46:00 +00:00
mio_htrd_close ( cli - > htrd ) ;
cli - > htrd = MIO_NULL ;
2020-05-06 09:28:36 +00:00
}
2020-05-12 11:46:00 +00:00
2020-05-12 17:53:19 +00:00
CLIL_UNLINK_CLI_CLEAN ( cli ) ;
/* are these needed? not symmetrical if done here.
* these fields are copied from the listener socket upon accept .
* init_client ( ) doesn ' t fill in these fields . let ' s comment out these lines
cli - > sck = MIO_NULL ;
cli - > htts = MIO_NULL ;
*/
2020-05-06 09:28:36 +00:00
}
/* ------------------------------------------------------------------------ */
2020-05-15 06:18:49 +00:00
static int listener_on_read ( mio_dev_sck_t * sck , const void * buf , mio_iolen_t len , const mio_skad_t * srcaddr )
2020-05-05 15:12:08 +00:00
{
2020-05-15 06:18:49 +00:00
/* unlike the function name, this callback is set on both the listener and the client.
* however , it must never be triggered for the listener */
2020-05-16 19:12:10 +00:00
mio_t * mio = sck - > mio ;
2020-05-12 11:46:00 +00:00
mio_svc_htts_cli_t * cli = mio_dev_sck_getxtn ( sck ) ;
2020-05-19 18:00:49 +00:00
mio_oow_t rem ;
2020-05-15 06:18:49 +00:00
int x ;
2020-05-16 19:12:10 +00:00
MIO_ASSERT ( mio , sck ! = cli - > htts - > lsck ) ;
2020-05-18 16:40:00 +00:00
MIO_ASSERT ( mio , cli - > rsrc = = MIO_NULL ) ; /* if a resource has been set, the resource must take over this handler */
2020-05-15 06:18:49 +00:00
2020-05-16 19:12:10 +00:00
if ( len < = - 1 )
{
2020-05-20 10:25:12 +00:00
MIO_DEBUG3 ( mio , " HTTS(%p) - unable to read client %p(%d) \n " , cli - > htts , sck , ( int ) sck - > hnd ) ;
2020-05-16 19:12:10 +00:00
goto oops ;
}
2020-05-20 10:25:12 +00:00
if ( len = = 0 )
{
MIO_DEBUG3 ( mio , " HTTS(%p) - EOF on client %p(%d) \n " , cli - > htts , sck , ( int ) sck - > hnd ) ;
goto oops ;
}
2020-05-16 19:12:10 +00:00
2020-05-19 18:00:49 +00:00
if ( ( x = mio_htrd_feed ( cli - > htrd , buf , len , & rem ) ) < = - 1 )
2020-05-05 15:12:08 +00:00
{
2020-05-15 06:18:49 +00:00
printf ( " ** HTTS - client htrd feed failure socket(%p) - %d \n " , sck , x ) ;
2020-05-18 16:40:00 +00:00
goto oops ;
2020-05-15 06:18:49 +00:00
}
2020-05-05 15:12:08 +00:00
2020-05-19 18:00:49 +00:00
if ( rem > 0 )
{
if ( cli - > rsrc )
{
/* TODO store this to client buffer. once the current resource is completed, arrange to call on_read() with it */
}
else
{
/* TODO: no resource in action. so feed one more time */
}
}
2020-05-05 15:12:08 +00:00
return 0 ;
2020-05-16 19:12:10 +00:00
oops :
2020-05-20 10:25:12 +00:00
printf ( " HALTING CLIENT SOCKEXXT %p \n " , sck ) ;
2020-05-16 19:12:10 +00:00
mio_dev_sck_halt ( sck ) ;
2020-05-18 16:40:00 +00:00
return 0 ;
2020-05-05 15:12:08 +00:00
}
2020-05-15 06:18:49 +00:00
static int listener_on_write ( mio_dev_sck_t * sck , mio_iolen_t wrlen , void * wrctx , const mio_skad_t * dstaddr )
2020-05-05 15:12:08 +00:00
{
2020-05-15 06:18:49 +00:00
mio_svc_htts_cli_t * cli = mio_dev_sck_getxtn ( sck ) ;
MIO_ASSERT ( sck - > mio , sck ! = cli - > htts - > lsck ) ;
2020-05-18 16:40:00 +00:00
MIO_ASSERT ( sck - > mio , cli - > rsrc = = MIO_NULL ) ; /* if a resource has been set, the resource must take over this handler */
2020-05-10 16:20:39 +00:00
return 0 ;
2020-05-05 15:12:08 +00:00
}
static void listener_on_connect ( mio_dev_sck_t * sck )
{
2020-05-12 11:46:00 +00:00
mio_svc_htts_cli_t * cli = mio_dev_sck_getxtn ( sck ) ; /* the contents came from the listening socket */
2020-05-05 15:12:08 +00:00
if ( sck - > state & MIO_DEV_SCK_ACCEPTED )
{
/* accepted a new client */
2020-05-12 17:53:19 +00:00
MIO_DEBUG3 ( sck - > mio , " HTTS(%p) - accepted... %p %d \n " , cli - > htts , sck , sck - > hnd ) ;
2020-05-05 15:12:08 +00:00
2020-05-12 11:46:00 +00:00
if ( init_client ( cli , sck ) < = - 1 )
2020-05-05 15:12:08 +00:00
{
2020-05-24 07:13:42 +00:00
MIO_DEBUG2 ( cli - > htts - > mio , " HTTS(%p) - halting client(%p) for client intiaialization failure \n " , cli - > htts , sck ) ;
2020-05-05 15:12:08 +00:00
mio_dev_sck_halt ( sck ) ;
}
}
else if ( sck - > state & MIO_DEV_SCK_CONNECTED )
{
/* this will never be triggered as the listing socket never call mio_dev_sck_connect() */
2020-05-12 17:53:19 +00:00
MIO_DEBUG3 ( sck - > mio , " ** HTTS(%p) - connected... %p %d \n " , cli - > htts , sck , sck - > hnd ) ;
2020-05-05 15:12:08 +00:00
}
/* MIO_DEV_SCK_CONNECTED must not be seen here as this is only for the listener socket */
}
static void listener_on_disconnect ( mio_dev_sck_t * sck )
{
2020-05-20 10:25:12 +00:00
mio_t * mio = sck - > mio ;
2020-05-12 11:46:00 +00:00
mio_svc_htts_cli_t * cli = mio_dev_sck_getxtn ( sck ) ;
2020-05-05 15:12:08 +00:00
switch ( MIO_DEV_SCK_GET_PROGRESS ( sck ) )
{
case MIO_DEV_SCK_CONNECTING :
2020-05-07 10:10:33 +00:00
/* only for connecting sockets */
2020-05-20 10:25:12 +00:00
MIO_DEBUG1 ( mio , " OUTGOING SESSION DISCONNECTED - FAILED TO CONNECT (%d) TO REMOTE SERVER \n " , ( int ) sck - > hnd ) ;
2020-05-05 15:12:08 +00:00
break ;
case MIO_DEV_SCK_CONNECTING_SSL :
2020-05-07 10:10:33 +00:00
/* only for connecting sockets */
2020-05-20 10:25:12 +00:00
MIO_DEBUG1 ( mio , " OUTGOING SESSION DISCONNECTED - FAILED TO SSL-CONNECT (%d) TO REMOTE SERVER \n " , ( int ) sck - > hnd ) ;
2020-05-05 15:12:08 +00:00
break ;
2020-05-07 10:10:33 +00:00
case MIO_DEV_SCK_CONNECTED :
/* only for connecting sockets */
2020-05-20 10:25:12 +00:00
MIO_DEBUG1 ( mio , " OUTGOING CLIENT CONNECTION GOT TORN DOWN %p(%d)....... \n " , ( int ) sck - > hnd ) ;
2020-05-05 15:12:08 +00:00
break ;
2020-05-07 10:10:33 +00:00
case MIO_DEV_SCK_LISTENING :
2020-05-20 10:25:12 +00:00
MIO_DEBUG2 ( mio , " LISTNER SOCKET %p(%d) - SHUTTUING DOWN \n " , sck , ( int ) sck - > hnd ) ;
2020-05-05 15:12:08 +00:00
break ;
2020-05-12 17:53:19 +00:00
case MIO_DEV_SCK_ACCEPTING_SSL : /* special case. */
/* this progress code indicates that the ssl-level accept failed.
* on_disconnected ( ) with this code is called without corresponding on_connect ( ) .
* the cli extension are is not initialized yet */
2020-05-20 10:25:12 +00:00
MIO_ASSERT ( mio , sck ! = cli - > sck ) ;
MIO_ASSERT ( mio , cli - > sck = = cli - > htts - > lsck ) ; /* the field is a copy of the extension are of the listener socket. so it should point to the listner socket */
MIO_DEBUG2 ( mio , " LISTENER UNABLE TO SSL-ACCEPT CLIENT %p(%d) ....%p \n " , sck , ( int ) sck - > hnd ) ;
2020-05-12 17:53:19 +00:00
return ;
2020-05-05 15:12:08 +00:00
case MIO_DEV_SCK_ACCEPTED :
2020-05-07 10:10:33 +00:00
/* only for sockets accepted by the listeners. will never come here because
* the disconnect call for such sockets have been changed in listener_on_connect ( ) */
2020-05-20 10:25:12 +00:00
MIO_DEBUG2 ( mio , " ACCEPTED CLIENT SOCKET %p(%d) GOT DISCONNECTED....... \n " , sck , ( int ) sck - > hnd ) ;
2020-05-05 15:12:08 +00:00
break ;
default :
2020-05-20 10:25:12 +00:00
MIO_DEBUG2 ( mio , " SOCKET %p(%d) DISCONNECTED AFTER ALL....... \n " , sck , ( int ) sck - > hnd ) ;
2020-05-05 15:12:08 +00:00
break ;
}
2020-05-12 17:53:19 +00:00
if ( sck = = cli - > htts - > lsck )
{
/* the listener socket has these fields set to NULL */
2020-05-20 10:25:12 +00:00
MIO_ASSERT ( mio , cli - > htrd = = MIO_NULL ) ;
MIO_ASSERT ( mio , cli - > sbuf = = MIO_NULL ) ;
2020-05-07 04:32:32 +00:00
2020-05-20 10:25:12 +00:00
MIO_DEBUG2 ( mio , " HTTS(%p) - listener socket disconnect %p \n " , cli - > htts , sck ) ;
2020-05-12 17:53:19 +00:00
cli - > htts - > lsck = MIO_NULL ; /* let the htts service forget about this listening socket */
}
else
{
/* client socket */
2020-05-20 10:25:12 +00:00
MIO_DEBUG2 ( mio , " HTTS(%p) - client socket disconnect %p \n " , cli - > htts , sck ) ;
MIO_ASSERT ( mio , cli - > sck = = sck ) ;
2020-05-12 17:53:19 +00:00
fini_client ( cli ) ;
}
2020-05-05 15:12:08 +00:00
}
/* ------------------------------------------------------------------------ */
2020-05-20 16:14:36 +00:00
mio_svc_htts_t * mio_svc_htts_start ( mio_t * mio , const mio_skad_t * bind_addr , mio_svc_htts_proc_req_t proc_req )
2020-05-05 15:12:08 +00:00
{
mio_svc_htts_t * htts = MIO_NULL ;
union
{
mio_dev_sck_make_t m ;
mio_dev_sck_bind_t b ;
mio_dev_sck_listen_t l ;
} info ;
2020-05-12 11:46:00 +00:00
mio_svc_htts_cli_t * cli ;
2020-05-05 15:12:08 +00:00
htts = ( mio_svc_htts_t * ) mio_callocmem ( mio , MIO_SIZEOF ( * htts ) ) ;
if ( MIO_UNLIKELY ( ! htts ) ) goto oops ;
htts - > mio = mio ;
htts - > svc_stop = mio_svc_htts_stop ;
2020-05-20 16:14:36 +00:00
htts - > proc_req = proc_req ;
2020-05-05 15:12:08 +00:00
MIO_MEMSET ( & info , 0 , MIO_SIZEOF ( info ) ) ;
switch ( mio_skad_family ( bind_addr ) )
{
case MIO_AF_INET :
info . m . type = MIO_DEV_SCK_TCP4 ;
break ;
case MIO_AF_INET6 :
info . m . type = MIO_DEV_SCK_TCP6 ;
break ;
default :
mio_seterrnum ( mio , MIO_EINVAL ) ;
goto oops ;
}
MIO_MEMSET ( & info , 0 , MIO_SIZEOF ( info ) ) ;
info . m . on_write = listener_on_write ;
info . m . on_read = listener_on_read ;
info . m . on_connect = listener_on_connect ;
info . m . on_disconnect = listener_on_disconnect ;
2020-05-12 11:46:00 +00:00
htts - > lsck = mio_dev_sck_make ( mio , MIO_SIZEOF ( * cli ) , & info . m ) ;
2020-05-05 15:12:08 +00:00
if ( ! htts - > lsck ) goto oops ;
2020-05-12 11:46:00 +00:00
/* the name 'cli' for the listening socket is awkard.
2020-05-12 17:53:19 +00:00
* the listing socket will use the htts and sck fields for tracking only .
2020-05-12 11:46:00 +00:00
* each accepted client socket gets the extension size for this size as well .
* most of other fields are used for client management */
cli = ( mio_svc_htts_cli_t * ) mio_dev_sck_getxtn ( htts - > lsck ) ;
cli - > htts = htts ;
2020-05-12 17:53:19 +00:00
cli - > sck = htts - > lsck ;
2020-05-05 15:12:08 +00:00
MIO_MEMSET ( & info , 0 , MIO_SIZEOF ( info ) ) ;
info . b . localaddr = * bind_addr ;
2020-05-24 01:18:32 +00:00
info . b . options = MIO_DEV_SCK_BIND_REUSEADDR | MIO_DEV_SCK_BIND_REUSEPORT | MIO_DEV_SCK_BIND_IGNERR ;
2020-05-15 06:18:49 +00:00
/*info.b.options |= MIO_DEV_SCK_BIND_SSL; */
2020-05-12 17:53:19 +00:00
info . b . ssl_certfile = " localhost.crt " ;
info . b . ssl_keyfile = " localhost.key " ;
2020-05-05 15:12:08 +00:00
if ( mio_dev_sck_bind ( htts - > lsck , & info . b ) < = - 1 ) goto oops ;
2020-05-07 10:10:33 +00:00
MIO_MEMSET ( & info , 0 , MIO_SIZEOF ( info ) ) ;
info . l . options = MIO_DEV_SCK_LISTEN_LENIENT ;
2020-05-05 15:12:08 +00:00
info . l . backlogs = 255 ;
2020-05-07 10:10:33 +00:00
MIO_INIT_NTIME ( & info . l . accept_tmout , 5 , 1 ) ;
2020-05-05 15:12:08 +00:00
if ( mio_dev_sck_listen ( htts - > lsck , & info . l ) < = - 1 ) goto oops ;
2020-05-07 10:10:33 +00:00
mio_fmttobcstr ( htts - > mio , htts - > server_name_buf , MIO_COUNTOF ( htts - > server_name_buf ) , " %s-%d.%d.%d " ,
MIO_PACKAGE_NAME , ( int ) MIO_PACKAGE_VERSION_MAJOR , ( int ) MIO_PACKAGE_VERSION_MINOR , ( int ) MIO_PACKAGE_VERSION_PATCH ) ;
htts - > server_name = htts - > server_name_buf ;
2020-05-05 15:12:08 +00:00
MIO_SVCL_APPEND_SVC ( & mio - > actsvc , ( mio_svc_t * ) htts ) ;
2020-05-12 11:46:00 +00:00
CLIL_INIT ( & htts - > cli ) ;
2020-05-07 10:10:33 +00:00
2020-05-15 06:18:49 +00:00
MIO_DEBUG3 ( mio , " HTTS - STARTED SERVICE %p - LISTENER SOCKET %p(%d) \n " , htts , htts - > lsck , ( int ) htts - > lsck - > hnd ) ;
2020-05-05 15:12:08 +00:00
return htts ;
oops :
if ( htts )
{
if ( htts - > lsck ) mio_dev_sck_kill ( htts - > lsck ) ;
mio_freemem ( mio , htts ) ;
}
return MIO_NULL ;
}
void mio_svc_htts_stop ( mio_svc_htts_t * htts )
{
mio_t * mio = htts - > mio ;
2020-05-15 06:18:49 +00:00
MIO_DEBUG3 ( mio , " HTTS - STOPPING SERVICE %p - LISTENER SOCKET %p(%d) \n " , htts , htts - > lsck , ( int ) ( htts - > lsck ? htts - > lsck - > hnd : - 1 ) ) ;
2020-05-07 10:10:33 +00:00
/* htts->lsck may be null if the socket has been destroyed for operational error and
* forgotten in the disconnect callback thereafter */
2020-05-05 15:12:08 +00:00
if ( htts - > lsck ) mio_dev_sck_kill ( htts - > lsck ) ;
2020-05-07 10:10:33 +00:00
2020-05-12 11:46:00 +00:00
while ( ! CLIL_IS_EMPTY ( & htts - > cli ) )
{
mio_svc_htts_cli_t * cli = CLIL_FIRST_CLI ( & htts - > cli ) ;
mio_dev_sck_kill ( cli - > sck ) ;
}
2020-05-05 15:12:08 +00:00
MIO_SVCL_UNLINK_SVC ( htts ) ;
2020-05-12 11:46:00 +00:00
if ( htts - > server_name & & htts - > server_name ! = htts - > server_name_buf ) mio_freemem ( mio , htts - > server_name ) ;
2020-05-05 15:12:08 +00:00
mio_freemem ( mio , htts ) ;
}
2020-05-07 10:10:33 +00:00
int mio_svc_htts_setservernamewithbcstr ( mio_svc_htts_t * htts , const mio_bch_t * name )
{
mio_t * mio = htts - > mio ;
mio_bch_t * tmp ;
if ( mio_copy_bcstr ( htts - > server_name_buf , MIO_COUNTOF ( htts - > server_name_buf ) , name ) = = mio_count_bcstr ( name ) )
{
tmp = htts - > server_name_buf ;
}
else
{
tmp = mio_dupbcstr ( mio , name , MIO_NULL ) ;
if ( ! tmp ) return - 1 ;
}
if ( htts - > server_name & & htts - > server_name ! = htts - > server_name_buf ) mio_freemem ( mio , htts - > server_name ) ;
htts - > server_name = tmp ;
return 0 ;
}
2020-05-05 15:12:08 +00:00
2020-05-08 09:48:26 +00:00
/* ----------------------------------------------------------------- */
2020-05-18 16:40:00 +00:00
mio_svc_htts_rsrc_t * mio_svc_htts_rsrc_make ( mio_svc_htts_t * htts , mio_oow_t rsrc_size , mio_svc_htts_rsrc_on_kill_t on_kill )
2020-05-07 15:47:33 +00:00
{
mio_t * mio = htts - > mio ;
2020-05-08 09:48:26 +00:00
mio_svc_htts_rsrc_t * rsrc ;
2020-05-18 16:40:00 +00:00
rsrc = mio_callocmem ( mio , rsrc_size ) ;
2020-05-08 09:48:26 +00:00
if ( MIO_UNLIKELY ( ! rsrc ) ) return MIO_NULL ;
rsrc - > htts = htts ;
2020-05-18 16:40:00 +00:00
rsrc - > rsrc_size = rsrc_size ;
rsrc - > rsrc_refcnt = 0 ;
rsrc - > rsrc_on_kill = on_kill ;
2020-05-08 09:48:26 +00:00
return rsrc ;
}
void mio_svc_htts_rsrc_kill ( mio_svc_htts_rsrc_t * rsrc )
{
2020-05-15 06:18:49 +00:00
printf ( " RSRC KILL >>>>> htts=> %p \n " , rsrc - > htts ) ;
2020-05-08 09:48:26 +00:00
mio_t * mio = rsrc - > htts - > mio ;
2020-05-18 16:40:00 +00:00
if ( rsrc - > rsrc_on_kill ) rsrc - > rsrc_on_kill ( rsrc ) ;
2020-05-08 09:48:26 +00:00
mio_freemem ( mio , rsrc ) ;
}
# if defined(MIO_HAVE_INLINE)
static MIO_INLINE void * mio_svc_htts_rsrc_getxtn ( mio_svc_htts_rsrc_t * rsrc ) { return rsrc + 1 ; }
# else
# define mio_svc_htts_rsrc_getxtn(rsrc) ((void*)((mio_svc_htts_rsrc_t*)rsrc + 1))
# endif
2020-05-12 11:46:00 +00:00
/* ----------------------------------------------------------------- */
2020-05-16 04:52:06 +00:00
enum cgi_state_res_mode_t
{
CGI_STATE_RES_MODE_CHUNKED ,
2020-05-16 19:52:50 +00:00
CGI_STATE_RES_MODE_CLOSE ,
CGI_STATE_RES_MODE_LENGTH
2020-05-16 04:52:06 +00:00
} ;
typedef enum cgi_state_res_mode_t cgi_state_res_mode_t ;
2020-05-16 19:12:10 +00:00
# define CGI_STATE_PENDING_IO_THRESHOLD 5
2020-05-15 06:18:49 +00:00
2020-05-19 09:11:39 +00:00
# define CGI_STATE_OVER_READ_FROM_CLIENT (1 << 0)
# define CGI_STATE_OVER_READ_FROM_PEER (1 << 1)
# define CGI_STATE_OVER_WRITE_TO_CLIENT (1 << 2)
# define CGI_STATE_OVER_WRITE_TO_PEER (1 << 3)
# define CGI_STATE_OVER_ALL (CGI_STATE_OVER_READ_FROM_CLIENT | CGI_STATE_OVER_READ_FROM_PEER | CGI_STATE_OVER_WRITE_TO_CLIENT | CGI_STATE_OVER_WRITE_TO_PEER)
2020-05-15 06:18:49 +00:00
struct cgi_state_t
{
2020-05-18 16:40:00 +00:00
MIO_SVC_HTTS_RSRC_HEADER ;
2020-05-15 06:18:49 +00:00
mio_oow_t num_pending_writes_to_client ;
mio_oow_t num_pending_writes_to_peer ;
mio_dev_pro_t * peer ;
mio_htrd_t * peer_htrd ;
2020-05-19 09:11:39 +00:00
mio_svc_htts_cli_t * client ;
2020-05-16 19:12:10 +00:00
mio_http_version_t req_version ; /* client request */
2020-05-15 06:18:49 +00:00
2020-05-19 09:11:39 +00:00
unsigned int over : 4 ; /* must be large enough to accomodate CGI_STATE_OVER_ALL */
unsigned int keep_alive : 1 ;
2020-05-18 08:59:25 +00:00
unsigned int req_content_length_unlimited : 1 ;
2020-05-20 10:25:12 +00:00
unsigned int ever_attempted_to_write_to_client : 1 ;
unsigned int client_disconnected : 1 ;
2020-05-18 08:59:25 +00:00
mio_oow_t req_content_length ; /* client request content length */
cgi_state_res_mode_t res_mode_to_cli ;
2020-05-16 04:52:06 +00:00
2020-05-19 09:11:39 +00:00
mio_dev_sck_on_read_t client_org_on_read ;
mio_dev_sck_on_write_t client_org_on_write ;
2020-05-20 10:25:12 +00:00
mio_dev_sck_on_disconnect_t client_org_on_disconnect ;
2020-05-15 06:18:49 +00:00
} ;
typedef struct cgi_state_t cgi_state_t ;
struct cgi_peer_xtn_t
{
cgi_state_t * state ;
} ;
typedef struct cgi_peer_xtn_t cgi_peer_xtn_t ;
2020-05-18 16:40:00 +00:00
static void cgi_state_halt_participating_devices ( cgi_state_t * cgi_state )
2020-05-16 19:12:10 +00:00
{
2020-05-19 09:11:39 +00:00
MIO_ASSERT ( cgi_state - > client - > htts - > mio , cgi_state - > client ! = MIO_NULL ) ;
MIO_ASSERT ( cgi_state - > client - > htts - > mio , cgi_state - > client - > sck ! = MIO_NULL ) ;
2020-05-16 19:12:10 +00:00
2020-05-20 10:25:12 +00:00
MIO_DEBUG4 ( cgi_state - > client - > htts - > mio , " HTTS(%p) - Halting participating devices in cgi state %p(client=%p,peer=%p) \n " , cgi_state - > client - > htts , cgi_state , cgi_state - > client - > sck , cgi_state - > peer ) ;
2020-05-16 19:12:10 +00:00
2020-05-19 09:11:39 +00:00
mio_dev_sck_halt ( cgi_state - > client - > sck ) ;
2020-05-16 19:12:10 +00:00
/* check for peer as it may not have been started */
if ( cgi_state - > peer ) mio_dev_pro_halt ( cgi_state - > peer ) ;
}
2020-05-17 18:09:19 +00:00
2020-05-18 08:59:25 +00:00
static int cgi_state_write_to_client ( cgi_state_t * cgi_state , const void * data , mio_iolen_t dlen )
{
2020-05-19 18:00:49 +00:00
cgi_state - > ever_attempted_to_write_to_client = 1 ;
2020-05-18 08:59:25 +00:00
cgi_state - > num_pending_writes_to_client + + ;
2020-05-19 09:11:39 +00:00
if ( mio_dev_sck_write ( cgi_state - > client - > sck , data , dlen , MIO_NULL , MIO_NULL ) < = - 1 )
2020-05-18 08:59:25 +00:00
{
cgi_state - > num_pending_writes_to_client - - ;
return - 1 ;
}
if ( cgi_state - > num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD )
{
if ( mio_dev_pro_read ( cgi_state - > peer , MIO_DEV_PRO_OUT , 0 ) < = - 1 ) return - 1 ;
}
return 0 ;
}
static int cgi_state_writev_to_client ( cgi_state_t * cgi_state , mio_iovec_t * iov , mio_iolen_t iovcnt )
{
2020-05-19 18:00:49 +00:00
cgi_state - > ever_attempted_to_write_to_client = 1 ;
2020-05-18 08:59:25 +00:00
cgi_state - > num_pending_writes_to_client + + ;
2020-05-19 09:11:39 +00:00
if ( mio_dev_sck_writev ( cgi_state - > client - > sck , iov , iovcnt , MIO_NULL , MIO_NULL ) < = - 1 )
2020-05-18 08:59:25 +00:00
{
cgi_state - > num_pending_writes_to_client - - ;
return - 1 ;
}
if ( cgi_state - > num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD )
{
if ( mio_dev_pro_read ( cgi_state - > peer , MIO_DEV_PRO_OUT , 0 ) < = - 1 ) return - 1 ;
}
return 0 ;
}
2020-05-24 07:13:42 +00:00
static int cgi_state_send_final_status_to_client ( cgi_state_t * cgi_state , int status_code , int force_close )
{
mio_svc_htts_cli_t * cli = cgi_state - > client ;
mio_bch_t dtbuf [ 64 ] ;
mio_svc_htts_fmtgmtime ( cli - > htts , MIO_NULL , dtbuf , MIO_COUNTOF ( dtbuf ) ) ;
if ( ! force_close ) force_close = ! cgi_state - > keep_alive ;
if ( mio_becs_fmt ( cli - > sbuf , " HTTP/%d.%d %d %hs \r \n Server: %hs \r \n Date: %s \r \n Connection: %hs \r \n Content-Length: 0 \r \n \r \n " ,
cgi_state - > req_version . major , cgi_state - > req_version . minor ,
status_code , mio_http_status_to_bcstr ( status_code ) ,
cli - > htts - > server_name , dtbuf ,
( force_close ? " close " : " keep-alive " ) ) = = ( mio_oow_t ) - 1 ) return - 1 ;
return ( cgi_state_write_to_client ( cgi_state , MIO_BECS_PTR ( cli - > sbuf ) , MIO_BECS_LEN ( cli - > sbuf ) ) < = - 1 | |
( force_close & & cgi_state_write_to_client ( cgi_state , MIO_NULL , 0 ) < = - 1 ) ) ? - 1 : 0 ;
}
2020-05-20 10:25:12 +00:00
static int cgi_state_write_last_chunk_to_client ( cgi_state_t * cgi_state )
{
2020-05-24 07:13:42 +00:00
if ( ! cgi_state - > ever_attempted_to_write_to_client )
{
if ( cgi_state_send_final_status_to_client ( cgi_state , 500 , 0 ) < = - 1 ) return - 1 ;
}
else
{
if ( cgi_state - > res_mode_to_cli = = CGI_STATE_RES_MODE_CHUNKED & &
cgi_state_write_to_client ( cgi_state , " 0 \r \n \r \n " , 5 ) < = - 1 ) return - 1 ;
}
2020-05-20 10:25:12 +00:00
if ( ! cgi_state - > keep_alive & & cgi_state_write_to_client ( cgi_state , MIO_NULL , 0 ) < = - 1 ) return - 1 ;
return 0 ;
}
2020-05-18 08:59:25 +00:00
static int cgi_state_write_to_peer ( cgi_state_t * cgi_state , const void * data , mio_iolen_t dlen )
{
cgi_state - > num_pending_writes_to_peer + + ;
if ( mio_dev_pro_write ( cgi_state - > peer , data , dlen , MIO_NULL ) < = - 1 )
{
cgi_state - > num_pending_writes_to_peer - - ;
return - 1 ;
}
/* TODO: check if it's already finished or something.. */
if ( cgi_state - > num_pending_writes_to_peer > CGI_STATE_PENDING_IO_THRESHOLD )
{
2020-05-19 09:11:39 +00:00
if ( mio_dev_sck_read ( cgi_state - > client - > sck , 0 ) < = - 1 ) return - 1 ;
2020-05-18 08:59:25 +00:00
}
return 0 ;
}
2020-05-19 09:11:39 +00:00
static MIO_INLINE void cgi_state_mark_over ( cgi_state_t * cgi_state , int over_bits )
{
2020-05-19 09:40:03 +00:00
unsigned int old_over ;
old_over = cgi_state - > over ;
2020-05-19 09:11:39 +00:00
cgi_state - > over | = over_bits ;
MIO_DEBUG5 ( cgi_state - > htts - > mio , " HTTS(%p) - client=%p peer=%p new-bits=%x over=%x \n " , cgi_state - > htts , cgi_state - > client - > sck , cgi_state - > peer , ( int ) over_bits , ( int ) cgi_state - > over ) ;
2020-05-19 09:40:03 +00:00
if ( ! ( old_over & CGI_STATE_OVER_READ_FROM_CLIENT ) & & ( cgi_state - > over & CGI_STATE_OVER_READ_FROM_CLIENT ) )
2020-05-19 09:11:39 +00:00
{
2020-05-24 07:13:42 +00:00
if ( mio_dev_sck_read ( cgi_state - > client - > sck , 0 ) < = - 1 )
{
MIO_DEBUG2 ( cgi_state - > htts - > mio , " HTTS(%p) - halting client(%p) for failure to disable input watching \n " , cgi_state - > htts , cgi_state - > client - > sck ) ;
mio_dev_sck_halt ( cgi_state - > client - > sck ) ;
}
2020-05-19 09:11:39 +00:00
}
2020-05-19 09:40:03 +00:00
if ( ! ( old_over & CGI_STATE_OVER_READ_FROM_PEER ) & & ( cgi_state - > over & CGI_STATE_OVER_READ_FROM_PEER ) )
2020-05-19 09:11:39 +00:00
{
2020-05-24 09:38:08 +00:00
if ( cgi_state - > peer & & mio_dev_pro_read ( cgi_state - > peer , MIO_DEV_PRO_OUT , 0 ) < = - 1 )
{
MIO_DEBUG2 ( cgi_state - > htts - > mio , " HTTS(%p) - halting peer(%p) for failure to disable input watching \n " , cgi_state - > htts , cgi_state - > peer ) ;
mio_dev_pro_halt ( cgi_state - > peer ) ;
}
2020-05-19 09:11:39 +00:00
}
2020-05-19 09:40:03 +00:00
if ( old_over ! = CGI_STATE_OVER_ALL & & cgi_state - > over = = CGI_STATE_OVER_ALL )
2020-05-19 09:11:39 +00:00
{
/* ready to stop */
2020-05-24 09:38:08 +00:00
if ( cgi_state - > peer )
{
MIO_DEBUG2 ( cgi_state - > htts - > mio , " HTTS(%p) - halting peer(%p) as it is unneeded \n " , cgi_state - > htts , cgi_state - > peer ) ;
mio_dev_pro_halt ( cgi_state - > peer ) ;
}
2020-05-19 09:11:39 +00:00
if ( cgi_state - > keep_alive )
{
/* how to arrange to delete this cgi_state object and put the socket back to the normal waiting state??? */
2020-05-20 10:25:12 +00:00
MIO_ASSERT ( cgi_state - > htts - > mio , cgi_state - > client - > rsrc = = ( mio_svc_htts_rsrc_t * ) cgi_state ) ;
2020-05-19 18:00:49 +00:00
2020-05-20 10:25:12 +00:00
printf ( " DETACHING FROM THE MAIN CLIENT RSRC... state -> %p \n " , cgi_state - > client - > rsrc ) ;
MIO_SVC_HTTS_RSRC_DETACH ( cgi_state - > client - > rsrc ) ;
/* cgi_state must not be access from here down as it could have been destroyed */
2020-05-19 09:11:39 +00:00
}
else
{
2020-05-24 07:13:42 +00:00
MIO_DEBUG2 ( cgi_state - > htts - > mio , " HTTS(%p) - halting client(%p) for no keep-alive \n " , cgi_state - > htts , cgi_state - > client - > sck ) ;
2020-05-19 09:11:39 +00:00
mio_dev_sck_shutdown ( cgi_state - > client - > sck , MIO_DEV_SCK_SHUTDOWN_WRITE ) ;
mio_dev_sck_halt ( cgi_state - > client - > sck ) ;
}
}
}
2020-05-15 06:18:49 +00:00
static void cgi_state_on_kill ( cgi_state_t * cgi_state )
{
printf ( " **** CGI_STATE_ON_KILL \n " ) ;
if ( cgi_state - > peer )
{
2020-05-17 18:09:19 +00:00
cgi_peer_xtn_t * cgi_peer = mio_dev_pro_getxtn ( cgi_state - > peer ) ;
2020-05-19 18:00:49 +00:00
cgi_peer - > state = MIO_NULL ; /* cgi_peer->state many not be NULL if the resource is killed regardless of the reference count */
2020-05-17 18:09:19 +00:00
2020-05-15 06:18:49 +00:00
mio_dev_pro_kill ( cgi_state - > peer ) ;
cgi_state - > peer = MIO_NULL ;
}
if ( cgi_state - > peer_htrd )
{
2020-05-17 18:09:19 +00:00
cgi_peer_xtn_t * cgi_peer = mio_htrd_getxtn ( cgi_state - > peer_htrd ) ;
2020-05-19 18:00:49 +00:00
cgi_peer - > state = MIO_NULL ; /* cgi_peer->state many not be NULL if the resource is killed regardless of the reference count */
2020-05-17 18:09:19 +00:00
2020-05-15 06:18:49 +00:00
mio_htrd_close ( cgi_state - > peer_htrd ) ;
cgi_state - > peer_htrd = MIO_NULL ;
}
2020-05-19 09:11:39 +00:00
if ( cgi_state - > client_org_on_read )
2020-05-15 06:18:49 +00:00
{
2020-05-19 09:11:39 +00:00
cgi_state - > client - > sck - > on_read = cgi_state - > client_org_on_read ;
cgi_state - > client_org_on_read = MIO_NULL ;
2020-05-15 06:18:49 +00:00
}
2020-05-19 09:11:39 +00:00
if ( cgi_state - > client_org_on_write )
2020-05-15 06:18:49 +00:00
{
2020-05-19 09:11:39 +00:00
cgi_state - > client - > sck - > on_write = cgi_state - > client_org_on_write ;
cgi_state - > client_org_on_write = MIO_NULL ;
2020-05-15 06:18:49 +00:00
}
2020-05-16 19:12:10 +00:00
2020-05-20 10:25:12 +00:00
if ( cgi_state - > client_org_on_disconnect )
{
cgi_state - > client - > sck - > on_disconnect = cgi_state - > client_org_on_disconnect ;
cgi_state - > client_org_on_disconnect = MIO_NULL ;
}
2020-05-19 09:11:39 +00:00
mio_htrd_setrecbs ( cgi_state - > client - > htrd , & client_htrd_recbs ) ; /* restore the callbacks */
2020-05-20 10:25:12 +00:00
if ( ! cgi_state - > client_disconnected )
2020-05-19 18:00:49 +00:00
{
2020-05-20 10:25:12 +00:00
printf ( " ENABLING INPUT WATCHING on CLIENT %p. \n " , cgi_state - > client - > sck ) ;
if ( ! cgi_state - > keep_alive | | mio_dev_sck_read ( cgi_state - > client - > sck , 1 ) < = - 1 )
{
2020-05-24 07:13:42 +00:00
MIO_DEBUG2 ( cgi_state - > htts - > mio , " HTTS(%p) - halting client(%p) for failure to enable input watching \n " , cgi_state - > htts , cgi_state - > client - > sck ) ;
2020-05-20 10:25:12 +00:00
mio_dev_sck_halt ( cgi_state - > client - > sck ) ;
}
2020-05-19 18:00:49 +00:00
}
2020-05-20 10:25:12 +00:00
printf ( " **** CGI_STATE_ON_KILL DONE \n " ) ;
2020-05-15 06:18:49 +00:00
}
2020-05-12 11:46:00 +00:00
static void cgi_peer_on_close ( mio_dev_pro_t * pro , mio_dev_pro_sid_t sid )
{
mio_t * mio = pro - > mio ;
2020-05-15 06:18:49 +00:00
cgi_peer_xtn_t * cgi_peer = mio_dev_pro_getxtn ( pro ) ;
2020-05-16 19:12:10 +00:00
cgi_state_t * cgi_state = cgi_peer - > state ;
2020-05-17 18:09:19 +00:00
if ( ! cgi_state ) return ; /* cgi state already gone */
2020-05-19 09:11:39 +00:00
switch ( sid )
2020-05-15 06:18:49 +00:00
{
2020-05-19 09:11:39 +00:00
case MIO_DEV_PRO_MASTER :
MIO_DEBUG3 ( mio , " HTTS(%p) - peer %p(pid=%d) closing master \n " , cgi_state - > client - > htts , pro , ( int ) pro - > child_pid ) ;
2020-05-19 18:00:49 +00:00
cgi_state - > peer = MIO_NULL ; /* clear this peer from the state */
2020-05-20 10:25:12 +00:00
MIO_ASSERT ( mio , cgi_peer - > state ! = MIO_NULL ) ;
printf ( " DETACHING FROM CGI PEER DEVICE.....................%p %d \n " , cgi_peer - > state , ( int ) cgi_peer - > state - > rsrc_refcnt ) ;
MIO_SVC_HTTS_RSRC_DETACH ( cgi_peer - > state ) ;
if ( cgi_state - > peer_htrd )
{
/* once this peer device is closed, peer's htrd is also never used.
* it ' s safe to detach the extra information attached on the htrd object . */
cgi_peer = mio_htrd_getxtn ( cgi_state - > peer_htrd ) ;
MIO_ASSERT ( mio , cgi_peer - > state ! = MIO_NULL ) ;
printf ( " DETACHING FROM CGI PEER HTRD.....................%p %d \n " , cgi_peer - > state , ( int ) cgi_peer - > state - > rsrc_refcnt ) ;
MIO_SVC_HTTS_RSRC_DETACH ( cgi_peer - > state ) ;
}
2020-05-19 09:11:39 +00:00
break ;
2020-05-17 18:09:19 +00:00
2020-05-19 09:11:39 +00:00
case MIO_DEV_PRO_OUT :
MIO_ASSERT ( mio , cgi_state - > peer = = pro ) ;
MIO_DEBUG4 ( mio , " HTTS(%p) - peer %p(pid=%d) closing slave[%d] \n " , cgi_state - > client - > htts , pro , ( int ) pro - > child_pid , sid ) ;
if ( ! ( cgi_state - > over & CGI_STATE_OVER_READ_FROM_PEER ) )
2020-05-18 08:59:25 +00:00
{
2020-05-20 10:25:12 +00:00
if ( cgi_state_write_last_chunk_to_client ( cgi_state ) < = - 1 )
2020-05-19 09:11:39 +00:00
cgi_state_halt_participating_devices ( cgi_state ) ;
2020-05-20 10:25:12 +00:00
else
cgi_state_mark_over ( cgi_state , CGI_STATE_OVER_READ_FROM_PEER ) ;
2020-05-18 08:59:25 +00:00
}
2020-05-19 09:11:39 +00:00
break ;
case MIO_DEV_PRO_IN :
cgi_state_mark_over ( cgi_state , CGI_STATE_OVER_WRITE_TO_PEER ) ;
break ;
case MIO_DEV_PRO_ERR :
default :
MIO_DEBUG4 ( mio , " HTTS(%p) - peer %p(pid=%d) closing slave[%d] \n " , cgi_state - > client - > htts , pro , ( int ) pro - > child_pid , sid ) ;
/* do nothing */
break ;
2020-05-15 06:18:49 +00:00
}
2020-05-12 11:46:00 +00:00
}
static int cgi_peer_on_read ( mio_dev_pro_t * pro , mio_dev_pro_sid_t sid , const void * data , mio_iolen_t dlen )
{
mio_t * mio = pro - > mio ;
2020-05-15 06:18:49 +00:00
cgi_peer_xtn_t * cgi_peer = mio_dev_pro_getxtn ( pro ) ;
2020-05-16 04:52:06 +00:00
cgi_state_t * cgi_state = cgi_peer - > state ;
2020-05-12 11:46:00 +00:00
2020-05-16 19:12:10 +00:00
MIO_ASSERT ( mio , sid = = MIO_DEV_PRO_OUT ) ; /* since MIO_DEV_PRO_ERRTONUL is used, there should be no input from MIO_DEV_PRO_ERR */
2020-05-19 18:00:49 +00:00
MIO_ASSERT ( mio , cgi_state ! = MIO_NULL ) ;
2020-05-16 19:12:10 +00:00
2020-05-12 11:46:00 +00:00
if ( dlen < = - 1 )
{
2020-05-19 09:11:39 +00:00
MIO_DEBUG3 ( mio , " HTTPS(%p) - read error from peer %p(pid=%u) \n " , cgi_state - > client - > htts , pro , ( unsigned int ) pro - > child_pid ) ;
2020-05-16 19:12:10 +00:00
goto oops ;
2020-05-12 11:46:00 +00:00
}
2020-05-16 19:12:10 +00:00
if ( dlen = = 0 )
2020-05-12 11:46:00 +00:00
{
2020-05-19 09:11:39 +00:00
MIO_DEBUG3 ( mio , " HTTPS(%p) - EOF from peer %p(pid=%u) \n " , cgi_state - > client - > htts , pro , ( unsigned int ) pro - > child_pid ) ;
2020-05-16 19:12:10 +00:00
2020-05-19 09:40:03 +00:00
if ( ! ( cgi_state - > over & CGI_STATE_OVER_READ_FROM_PEER ) )
{
/* the cgi script could be misbehaviing.
* it still has to read more but EOF is read .
* otherwise client_peer_htrd_poke ( ) should have been called */
2020-05-20 10:25:12 +00:00
if ( cgi_state_write_last_chunk_to_client ( cgi_state ) < = - 1 ) goto oops ;
2020-05-19 09:40:03 +00:00
cgi_state_mark_over ( cgi_state , CGI_STATE_OVER_READ_FROM_PEER ) ;
}
2020-05-12 11:46:00 +00:00
}
2020-05-16 04:52:06 +00:00
else
2020-05-12 11:46:00 +00:00
{
2020-05-16 19:12:10 +00:00
mio_oow_t rem ;
2020-05-19 09:11:39 +00:00
2020-05-19 09:40:03 +00:00
MIO_ASSERT ( mio , ! ( cgi_state - > over & CGI_STATE_OVER_READ_FROM_PEER ) ) ;
2020-05-16 19:52:50 +00:00
if ( mio_htrd_feed ( cgi_state - > peer_htrd , data , dlen , & rem ) < = - 1 )
{
2020-05-19 18:00:49 +00:00
MIO_DEBUG3 ( mio , " HTTPS(%p) - unable to feed peer into to htrd - peer %p(pid=%u) \n " , cgi_state - > htts , pro , ( unsigned int ) pro - > child_pid ) ;
2020-05-20 10:25:12 +00:00
2020-05-19 18:00:49 +00:00
if ( ! cgi_state - > ever_attempted_to_write_to_client & &
2020-05-20 10:25:12 +00:00
! ( cgi_state - > over & CGI_STATE_OVER_WRITE_TO_CLIENT ) )
{
2020-05-24 07:13:42 +00:00
cgi_state_send_final_status_to_client ( cgi_state , 500 , 1 ) ; /* don't care about error because it jumps to oops below anyway */
2020-05-20 10:25:12 +00:00
}
goto oops ;
2020-05-16 19:52:50 +00:00
}
2020-05-19 09:11:39 +00:00
2020-05-16 19:52:50 +00:00
if ( rem > 0 )
{
/* If the script specifies Content-Length and produces longer data, it will come here */
2020-05-19 09:11:39 +00:00
printf ( " AAAAAAAAAAAAAAAAAa EEEEEXcessive DATA.................. \n " ) ;
/* TODO: or drop this request?? */
2020-05-19 18:00:49 +00:00
}
2020-05-16 19:12:10 +00:00
}
return 0 ;
oops :
2020-05-18 16:40:00 +00:00
cgi_state_halt_participating_devices ( cgi_state ) ;
2020-05-16 19:12:10 +00:00
return 0 ;
}
2020-05-20 14:53:05 +00:00
static int cgi_peer_capture_response_header ( mio_htre_t * req , const mio_bch_t * key , const mio_htre_hdrval_t * val , void * ctx )
2020-05-16 19:12:10 +00:00
{
mio_svc_htts_cli_t * cli = ( mio_svc_htts_cli_t * ) ctx ;
/* capture a header except Status, Connection, Transfer-Encoding, and Server */
if ( mio_comp_bcstr ( key , " Status " , 1 ) ! = 0 & &
mio_comp_bcstr ( key , " Connection " , 1 ) ! = 0 & &
mio_comp_bcstr ( key , " Transfer-Encoding " , 1 ) ! = 0 & &
2020-05-17 18:09:19 +00:00
mio_comp_bcstr ( key , " Server " , 1 ) ! = 0 & &
mio_comp_bcstr ( key , " Date " , 1 ) ! = 0 )
2020-05-16 19:12:10 +00:00
{
do
2020-05-16 04:52:06 +00:00
{
2020-05-16 19:12:10 +00:00
if ( mio_becs_cat ( cli - > sbuf , key ) = = ( mio_oow_t ) - 1 | |
mio_becs_cat ( cli - > sbuf , " : " ) = = ( mio_oow_t ) - 1 | |
mio_becs_cat ( cli - > sbuf , val - > ptr ) = = ( mio_oow_t ) - 1 | |
mio_becs_cat ( cli - > sbuf , " \r \n " ) = = ( mio_oow_t ) - 1 )
{
return - 1 ;
}
val = val - > next ;
}
while ( val ) ;
}
return 0 ;
}
2020-05-19 09:11:39 +00:00
2020-05-16 19:12:10 +00:00
static int cgi_peer_htrd_peek ( mio_htrd_t * htrd , mio_htre_t * req )
{
cgi_peer_xtn_t * cgi_peer = mio_htrd_getxtn ( htrd ) ;
cgi_state_t * cgi_state = cgi_peer - > state ;
2020-05-19 09:11:39 +00:00
mio_svc_htts_cli_t * cli = cgi_state - > client ;
2020-05-17 18:09:19 +00:00
mio_bch_t dtbuf [ 64 ] ;
2020-05-16 19:12:10 +00:00
int status_code ;
2020-05-16 19:52:50 +00:00
if ( req - > attr . content_length )
{
// TOOD: remove content_length if content_length is negative or not numeric.
cgi_state - > res_mode_to_cli = CGI_STATE_RES_MODE_LENGTH ;
}
2020-05-16 19:12:10 +00:00
if ( req - > attr . status )
{
}
else
{
}
status_code = 200 ;
printf ( " CGI PEER HTRD PEEK... \n " ) ;
2020-05-17 18:09:19 +00:00
mio_svc_htts_fmtgmtime ( cli - > htts , MIO_NULL , dtbuf , MIO_COUNTOF ( dtbuf ) ) ;
if ( mio_becs_fmt ( cli - > sbuf , " HTTP/%d.%d %d %hs \r \n Server: %hs \r \n Date: %hs \r \n " ,
2020-05-16 19:12:10 +00:00
cgi_state - > req_version . major , cgi_state - > req_version . minor ,
2020-05-17 18:09:19 +00:00
status_code , mio_http_status_to_bcstr ( status_code ) ,
cli - > htts - > server_name , dtbuf ) = = ( mio_oow_t ) - 1 ) return - 1 ;
2020-05-16 19:12:10 +00:00
2020-05-20 14:53:05 +00:00
if ( mio_htre_walkheaders ( req , cgi_peer_capture_response_header , cli ) < = - 1 ) return - 1 ;
2020-05-16 19:12:10 +00:00
switch ( cgi_state - > res_mode_to_cli )
{
case CGI_STATE_RES_MODE_CHUNKED :
if ( mio_becs_cat ( cli - > sbuf , " Transfer-Encoding: chunked \r \n " ) = = ( mio_oow_t ) - 1 ) return - 1 ;
/*if (mio_becs_cat(cli->sbuf, "Connection: keep-alive\r\n") == (mio_oow_t)-1) return -1;*/
break ;
case CGI_STATE_RES_MODE_CLOSE :
if ( mio_becs_cat ( cli - > sbuf , " Connection: close \r \n " ) = = ( mio_oow_t ) - 1 ) return - 1 ;
break ;
2020-05-16 19:52:50 +00:00
case CGI_STATE_RES_MODE_LENGTH :
2020-05-20 10:25:12 +00:00
if ( mio_becs_cat ( cli - > sbuf , ( cgi_state - > keep_alive ? " Connection: keep-alive \r \n " : " Connection: close \r \n " ) ) = = ( mio_oow_t ) - 1 ) return - 1 ;
2020-05-16 19:12:10 +00:00
}
if ( mio_becs_cat ( cli - > sbuf , " \r \n " ) = = ( mio_oow_t ) - 1 ) return - 1 ;
2020-05-24 07:13:42 +00:00
2020-05-18 08:59:25 +00:00
return cgi_state_write_to_client ( cgi_state , MIO_BECS_PTR ( cli - > sbuf ) , MIO_BECS_LEN ( cli - > sbuf ) ) ;
}
static int cgi_peer_htrd_poke ( mio_htrd_t * htrd , mio_htre_t * req )
{
/* client request got completed */
cgi_peer_xtn_t * cgi_peer = mio_htrd_getxtn ( htrd ) ;
cgi_state_t * cgi_state = cgi_peer - > state ;
2020-05-19 09:40:03 +00:00
printf ( " >> PEER RESPONSE COMPLETED \n " ) ;
2020-05-20 10:25:12 +00:00
if ( cgi_state_write_last_chunk_to_client ( cgi_state ) < = - 1 ) return - 1 ;
2020-05-19 09:40:03 +00:00
cgi_state_mark_over ( cgi_state , CGI_STATE_OVER_READ_FROM_PEER ) ;
return 0 ;
2020-05-16 19:12:10 +00:00
}
static int cgi_peer_htrd_push_content ( mio_htrd_t * htrd , mio_htre_t * req , const mio_bch_t * data , mio_oow_t dlen )
{
cgi_peer_xtn_t * cgi_peer = mio_htrd_getxtn ( htrd ) ;
cgi_state_t * cgi_state = cgi_peer - > state ;
2020-05-19 09:11:39 +00:00
MIO_ASSERT ( cgi_state - > client - > htts - > mio , htrd = = cgi_state - > peer_htrd ) ;
2020-05-16 19:12:10 +00:00
switch ( cgi_state - > res_mode_to_cli )
{
case CGI_STATE_RES_MODE_CHUNKED :
{
mio_iovec_t iov [ 3 ] ;
mio_bch_t lbuf [ 16 ] ;
mio_oow_t llen ;
2020-05-24 01:18:32 +00:00
/* mio_fmt_uintmax_to_bcstr() null-terminates the output. only MIO_COUNTOF(lbuf) - 1
* is enough to hold ' \r ' and ' \n ' at the back without ' \0 ' . */
llen = mio_fmt_uintmax_to_bcstr ( lbuf , MIO_COUNTOF ( lbuf ) - 1 , dlen , 16 | MIO_FMT_UINTMAX_UPPERCASE , 0 , ' \0 ' , MIO_NULL ) ;
2020-05-16 19:12:10 +00:00
lbuf [ llen + + ] = ' \r ' ;
lbuf [ llen + + ] = ' \n ' ;
iov [ 0 ] . iov_ptr = lbuf ;
iov [ 0 ] . iov_len = llen ;
2020-05-19 09:11:39 +00:00
iov [ 1 ] . iov_ptr = ( void * ) data ;
2020-05-16 19:12:10 +00:00
iov [ 1 ] . iov_len = dlen ;
iov [ 2 ] . iov_ptr = " \r \n " ;
iov [ 2 ] . iov_len = 2 ;
2020-05-16 04:52:06 +00:00
2020-05-18 08:59:25 +00:00
if ( cgi_state_writev_to_client ( cgi_state , iov , MIO_COUNTOF ( iov ) ) < = - 1 ) goto oops ;
2020-05-16 19:12:10 +00:00
break ;
2020-05-16 04:52:06 +00:00
}
2020-05-16 19:12:10 +00:00
case CGI_STATE_RES_MODE_CLOSE :
2020-05-16 19:52:50 +00:00
case CGI_STATE_RES_MODE_LENGTH :
2020-05-18 08:59:25 +00:00
if ( cgi_state_write_to_client ( cgi_state , data , dlen ) < = - 1 ) goto oops ;
break ;
2020-05-12 11:46:00 +00:00
}
2020-05-16 19:12:10 +00:00
if ( cgi_state - > num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD )
{
if ( mio_dev_pro_read ( cgi_state - > peer , MIO_DEV_PRO_OUT , 0 ) < = - 1 ) goto oops ;
}
2020-05-12 11:46:00 +00:00
return 0 ;
2020-05-16 19:12:10 +00:00
oops :
return - 1 ;
2020-05-12 11:46:00 +00:00
}
2020-05-16 19:12:10 +00:00
static mio_htrd_recbs_t cgi_peer_htrd_recbs =
{
cgi_peer_htrd_peek ,
2020-05-18 08:59:25 +00:00
cgi_peer_htrd_poke ,
2020-05-16 19:12:10 +00:00
cgi_peer_htrd_push_content
} ;
2020-05-18 08:59:25 +00:00
static int cgi_client_htrd_poke ( mio_htrd_t * htrd , mio_htre_t * req )
2020-05-17 18:09:19 +00:00
{
2020-05-18 08:59:25 +00:00
/* client request got completed */
2020-05-17 18:09:19 +00:00
htrd_xtn_t * htrdxtn = mio_htrd_getxtn ( htrd ) ;
mio_dev_sck_t * sck = htrdxtn - > sck ;
mio_svc_htts_cli_t * cli = mio_dev_sck_getxtn ( sck ) ;
2020-05-19 09:11:39 +00:00
cgi_state_t * cgi_state = ( cgi_state_t * ) cli - > rsrc ;
printf ( " >> CLIENT REQUEST COMPLETED \n " ) ;
2020-05-17 18:09:19 +00:00
2020-05-18 08:59:25 +00:00
/* indicate EOF to the client peer */
2020-05-19 09:40:03 +00:00
if ( cgi_state_write_to_peer ( cgi_state , MIO_NULL , 0 ) < = - 1 ) return - 1 ;
2020-05-19 09:11:39 +00:00
cgi_state_mark_over ( cgi_state , CGI_STATE_OVER_READ_FROM_CLIENT ) ;
2020-05-19 09:40:03 +00:00
return 0 ;
2020-05-18 08:59:25 +00:00
}
2020-05-17 18:09:19 +00:00
2020-05-18 08:59:25 +00:00
static int cgi_client_htrd_push_content ( mio_htrd_t * htrd , mio_htre_t * req , const mio_bch_t * data , mio_oow_t dlen )
{
htrd_xtn_t * htrdxtn = mio_htrd_getxtn ( htrd ) ;
mio_dev_sck_t * sck = htrdxtn - > sck ;
mio_svc_htts_cli_t * cli = mio_dev_sck_getxtn ( sck ) ;
2020-05-19 09:11:39 +00:00
cgi_state_t * cgi_state = ( cgi_state_t * ) cli - > rsrc ;
2020-05-17 18:09:19 +00:00
2020-05-18 08:59:25 +00:00
MIO_ASSERT ( sck - > mio , cli - > sck = = sck ) ;
return cgi_state_write_to_peer ( cgi_state , data , dlen ) ;
2020-05-17 18:09:19 +00:00
}
2020-05-12 11:46:00 +00:00
static int cgi_peer_on_write ( mio_dev_pro_t * pro , mio_iolen_t wrlen , void * wrctx )
{
mio_t * mio = pro - > mio ;
2020-05-15 06:18:49 +00:00
cgi_peer_xtn_t * cgi_peer = mio_dev_pro_getxtn ( pro ) ;
cgi_state_t * cgi_state = cgi_peer - > state ;
2020-05-16 19:12:10 +00:00
2020-05-24 17:58:08 +00:00
if ( cgi_state = = MIO_NULL ) return 0 ; /* there is nothing i can do. the cgi_state is being cleared or has been cleared already. */
2020-05-16 19:12:10 +00:00
MIO_ASSERT ( mio , cgi_state - > peer = = pro ) ;
2020-05-15 06:18:49 +00:00
2020-05-12 11:46:00 +00:00
if ( wrlen < = - 1 )
{
2020-05-19 09:11:39 +00:00
MIO_DEBUG3 ( mio , " HTTS(%p) - unable to write to peer %p(pid=%u) \n " , cgi_state - > client - > htts , pro , ( int ) pro - > child_pid ) ;
2020-05-16 19:12:10 +00:00
goto oops ;
2020-05-12 11:46:00 +00:00
}
2020-05-18 08:59:25 +00:00
else if ( wrlen = = 0 )
{
/* indicated EOF */
/* do nothing here as i didn't incremented num_pending_writes_to_peer when making the write request */
2020-05-19 09:11:39 +00:00
2020-05-18 08:59:25 +00:00
cgi_state - > num_pending_writes_to_peer - - ;
MIO_ASSERT ( mio , cgi_state - > num_pending_writes_to_peer = = 0 ) ;
2020-05-19 09:11:39 +00:00
MIO_DEBUG3 ( mio , " HTTS(%p) - indicated EOF to peer %p(pid=%u) \n " , cgi_state - > client - > htts , pro , ( int ) pro - > child_pid ) ;
2020-05-18 08:59:25 +00:00
/* indicated EOF to the peer side. i need no more data from the client side.
* i don ' t need to enable input watching in the client side either */
2020-05-19 09:11:39 +00:00
cgi_state_mark_over ( cgi_state , CGI_STATE_OVER_WRITE_TO_PEER ) ;
2020-05-18 08:59:25 +00:00
}
else
2020-05-15 06:18:49 +00:00
{
2020-05-18 08:59:25 +00:00
MIO_ASSERT ( mio , cgi_state - > num_pending_writes_to_peer > 0 ) ;
cgi_state - > num_pending_writes_to_peer - - ;
if ( cgi_state - > num_pending_writes_to_peer = = CGI_STATE_PENDING_IO_THRESHOLD )
{
2020-05-19 09:11:39 +00:00
if ( ! ( cgi_state - > over & CGI_STATE_OVER_READ_FROM_CLIENT ) & &
mio_dev_sck_read ( cgi_state - > client - > sck , 1 ) < = - 1 ) goto oops ;
}
if ( ( cgi_state - > over & CGI_STATE_OVER_READ_FROM_CLIENT ) & & cgi_state - > num_pending_writes_to_peer < = 0 )
{
cgi_state_mark_over ( cgi_state , CGI_STATE_OVER_WRITE_TO_PEER ) ;
2020-05-18 08:59:25 +00:00
}
2020-05-15 06:18:49 +00:00
}
2020-05-18 08:59:25 +00:00
2020-05-16 19:12:10 +00:00
return 0 ;
2020-05-15 06:18:49 +00:00
2020-05-16 19:12:10 +00:00
oops :
2020-05-18 16:40:00 +00:00
cgi_state_halt_participating_devices ( cgi_state ) ;
2020-05-12 11:46:00 +00:00
return 0 ;
}
2020-05-20 10:25:12 +00:00
static void cgi_client_on_disconnect ( mio_dev_sck_t * sck )
{
mio_svc_htts_cli_t * cli = mio_dev_sck_getxtn ( sck ) ;
cgi_state_t * cgi_state = ( cgi_state_t * ) cli - > rsrc ;
cgi_state - > client_disconnected = 1 ;
listener_on_disconnect ( sck ) ;
}
2020-05-12 11:46:00 +00:00
static int cgi_client_on_read ( mio_dev_sck_t * sck , const void * buf , mio_iolen_t len , const mio_skad_t * srcaddr )
{
2020-05-16 19:12:10 +00:00
mio_t * mio = sck - > mio ;
2020-05-12 11:46:00 +00:00
mio_svc_htts_cli_t * cli = mio_dev_sck_getxtn ( sck ) ;
2020-05-19 09:11:39 +00:00
cgi_state_t * cgi_state = ( cgi_state_t * ) cli - > rsrc ;
2020-05-15 06:18:49 +00:00
2020-05-16 19:12:10 +00:00
MIO_ASSERT ( mio , sck = = cli - > sck ) ;
2020-05-15 06:18:49 +00:00
if ( len < = - 1 )
2020-05-12 11:46:00 +00:00
{
/* read error */
2020-05-19 09:40:03 +00:00
MIO_DEBUG2 ( cli - > htts - > mio , " HTTPS(%p) - read error on client %p(%d) \n " , sck , ( int ) sck - > hnd ) ;
2020-05-15 06:18:49 +00:00
goto oops ;
2020-05-12 11:46:00 +00:00
}
2020-05-16 19:12:10 +00:00
if ( ! cgi_state - > peer )
{
/* the peer is gone */
goto oops ; /* do what? just return 0? */
}
if ( len = = 0 )
2020-05-12 11:46:00 +00:00
{
2020-05-16 19:12:10 +00:00
/* EOF on the client side. arrange to close */
2020-05-19 09:40:03 +00:00
MIO_DEBUG3 ( mio , " HTTPS(%p) - EOF from client %p(hnd=%d) \n " , cgi_state - > client - > htts , sck , ( int ) sck - > hnd ) ;
2020-05-19 18:00:49 +00:00
if ( ! ( cgi_state - > over & CGI_STATE_OVER_READ_FROM_CLIENT ) ) /* if this is true, EOF is received without cgi_client_htrd_poke() */
2020-05-19 09:40:03 +00:00
{
if ( cgi_state_write_to_peer ( cgi_state , MIO_NULL , 0 ) < = - 1 ) goto oops ;
cgi_state_mark_over ( cgi_state , CGI_STATE_OVER_READ_FROM_CLIENT ) ;
}
2020-05-15 06:18:49 +00:00
}
else
{
mio_oow_t rem ;
2020-05-19 09:40:03 +00:00
MIO_ASSERT ( mio , ! ( cgi_state - > over & CGI_STATE_OVER_READ_FROM_CLIENT ) ) ;
2020-05-19 09:11:39 +00:00
if ( mio_htrd_feed ( cli - > htrd , buf , len , & rem ) < = - 1 ) goto oops ;
2020-05-15 06:18:49 +00:00
2020-05-17 18:09:19 +00:00
if ( rem > 0 )
2020-05-15 06:18:49 +00:00
{
2020-05-19 18:00:49 +00:00
/* TODO store this to client buffer. once the current resource is completed, arrange to call on_read() with it */
2020-05-19 09:40:03 +00:00
printf ( " UUUUUUUUUUUUUUUUUUUUUUUUUUGGGGGHHHHHHHHHHHH .......... CGI CLIENT GIVING EXCESSIVE DATA AFTER CONTENTS... \n " ) ;
2020-05-15 06:18:49 +00:00
}
2020-05-12 11:46:00 +00:00
}
return 0 ;
2020-05-15 06:18:49 +00:00
oops :
2020-05-18 16:40:00 +00:00
cgi_state_halt_participating_devices ( cgi_state ) ;
2020-05-12 11:46:00 +00:00
return 0 ;
}
2020-05-15 06:18:49 +00:00
static int cgi_client_on_write ( mio_dev_sck_t * sck , mio_iolen_t wrlen , void * wrctx , const mio_skad_t * dstaddr )
2020-05-12 11:46:00 +00:00
{
2020-05-16 19:12:10 +00:00
mio_t * mio = sck - > mio ;
2020-05-15 06:18:49 +00:00
mio_svc_htts_cli_t * cli = mio_dev_sck_getxtn ( sck ) ;
2020-05-19 09:11:39 +00:00
cgi_state_t * cgi_state = ( cgi_state_t * ) cli - > rsrc ;
2020-05-15 06:18:49 +00:00
2020-05-16 19:12:10 +00:00
if ( wrlen < = - 1 )
{
2020-05-18 08:59:25 +00:00
MIO_DEBUG3 ( mio , " HTTPS(%p) - unable to write to client %p(%d) \n " , sck - > mio , sck , ( int ) sck - > hnd ) ;
2020-05-16 19:12:10 +00:00
goto oops ;
}
2020-05-18 08:59:25 +00:00
if ( wrlen = = 0 )
2020-05-16 19:12:10 +00:00
{
2020-05-18 08:59:25 +00:00
/* if the connect is keep-alive, this part may not be called */
cgi_state - > num_pending_writes_to_client - - ;
MIO_ASSERT ( mio , cgi_state - > num_pending_writes_to_client = = 0 ) ;
2020-05-19 09:11:39 +00:00
MIO_DEBUG3 ( mio , " HTTS(%p) - indicated EOF to client %p(%d) \n " , cgi_state - > client - > htts , sck , ( int ) sck - > hnd ) ;
2020-05-18 08:59:25 +00:00
/* since EOF has been indicated to the client, it must not write to the client any further.
* this also means that i don ' t need any data from the peer side either .
* i don ' t need to enable input watching on the peer side */
2020-05-19 09:11:39 +00:00
cgi_state_mark_over ( cgi_state , CGI_STATE_OVER_WRITE_TO_CLIENT ) ;
2020-05-16 19:12:10 +00:00
}
2020-05-18 08:59:25 +00:00
else
2020-05-16 19:12:10 +00:00
{
2020-05-18 08:59:25 +00:00
MIO_ASSERT ( mio , cgi_state - > num_pending_writes_to_client > 0 ) ;
cgi_state - > num_pending_writes_to_client - - ;
if ( cgi_state - > peer & & cgi_state - > num_pending_writes_to_client = = CGI_STATE_PENDING_IO_THRESHOLD )
{
2020-05-19 09:11:39 +00:00
if ( ! ( cgi_state - > over & CGI_STATE_OVER_READ_FROM_PEER ) & &
mio_dev_pro_read ( cgi_state - > peer , MIO_DEV_PRO_OUT , 1 ) < = - 1 ) goto oops ;
2020-05-18 08:59:25 +00:00
}
2020-05-19 09:11:39 +00:00
if ( ( cgi_state - > over & CGI_STATE_OVER_READ_FROM_PEER ) & & cgi_state - > num_pending_writes_to_client < = 0 )
2020-05-18 08:59:25 +00:00
{
2020-05-19 09:11:39 +00:00
cgi_state_mark_over ( cgi_state , CGI_STATE_OVER_WRITE_TO_CLIENT ) ;
2020-05-18 08:59:25 +00:00
}
2020-05-16 19:12:10 +00:00
}
2020-05-12 11:46:00 +00:00
2020-05-16 19:12:10 +00:00
return 0 ;
2020-05-12 11:46:00 +00:00
2020-05-16 19:12:10 +00:00
oops :
2020-05-18 16:40:00 +00:00
cgi_state_halt_participating_devices ( cgi_state ) ;
2020-05-15 06:18:49 +00:00
return 0 ;
}
2020-05-18 09:12:36 +00:00
static MIO_INLINE int get_request_content_length ( mio_htre_t * req , mio_oow_t * len )
2020-05-12 11:46:00 +00:00
{
2020-05-18 08:59:25 +00:00
if ( req - > flags & MIO_HTRE_ATTR_CHUNKED )
2020-05-15 06:18:49 +00:00
{
/* "Transfer-Encoding: chunked" take precedence over "Content-Length: XXX".
*
* [ RFC7230 ]
* If a message is received with both a Transfer - Encoding and a
* Content - Length header field , the Transfer - Encoding overrides the
* Content - Length . */
2020-05-18 09:12:36 +00:00
return 1 ; /* unable to determine content-length in advance. unlimited */
2020-05-15 06:18:49 +00:00
}
2020-05-18 09:12:36 +00:00
if ( req - > flags & MIO_HTRE_ATTR_LENGTH )
2020-05-15 06:18:49 +00:00
{
* len = req - > attr . content_length ;
}
else
{
2020-05-18 08:59:25 +00:00
/* If no Content-Length is specified in a request, it's Content-Length: 0 */
2020-05-15 06:18:49 +00:00
* len = 0 ;
}
2020-05-18 09:12:36 +00:00
return 0 ; /* limited to the length set in *len */
2020-05-12 11:46:00 +00:00
}
2020-05-20 10:25:12 +00:00
struct cgi_peer_fork_ctx_t
{
mio_svc_htts_cli_t * cli ;
mio_htre_t * req ;
const mio_bch_t * docroot ;
2020-05-20 16:14:36 +00:00
const mio_bch_t * script ;
mio_bch_t * actual_script ;
2020-05-20 10:25:12 +00:00
} ;
typedef struct cgi_peer_fork_ctx_t cgi_peer_fork_ctx_t ;
2020-05-20 14:53:05 +00:00
static int cgi_peer_capture_request_header ( mio_htre_t * req , const mio_bch_t * key , const mio_htre_hdrval_t * val , void * ctx )
{
mio_becs_t * dbuf = ( mio_becs_t * ) ctx ;
if ( mio_comp_bcstr ( key , " Connection " , 1 ) ! = 0 & &
mio_comp_bcstr ( key , " Transfer-Encoding " , 1 ) ! = 0 & &
mio_comp_bcstr ( key , " Content-Length " , 1 ) ! = 0 & &
mio_comp_bcstr ( key , " Expect " , 1 ) ! = 0 )
{
mio_oow_t val_offset ;
mio_bch_t * ptr ;
mio_becs_clear ( dbuf ) ;
if ( mio_becs_cpy ( dbuf , " HTTP_ " ) = = ( mio_oow_t ) - 1 | |
mio_becs_cat ( dbuf , key ) = = ( mio_oow_t ) - 1 | |
mio_becs_ccat ( dbuf , ' \0 ' ) = = ( mio_oow_t ) - 1 ) return - 1 ;
for ( ptr = MIO_BECS_PTR ( dbuf ) ; * ptr ; ptr + + )
{
* ptr = mio_to_bch_upper ( * ptr ) ;
if ( * ptr = = ' - ' ) * ptr = ' _ ' ;
}
val_offset = MIO_BECS_LEN ( dbuf ) ;
if ( mio_becs_cat ( dbuf , val - > ptr ) = = ( mio_oow_t ) - 1 ) return - 1 ;
val = val - > next ;
while ( val )
{
if ( mio_becs_cat ( dbuf , " , " ) = = ( mio_oow_t ) - 1 | |
mio_becs_cat ( dbuf , val - > ptr ) = = ( mio_oow_t ) - 1 ) return - 1 ;
val = val - > next ;
}
setenv ( MIO_BECS_PTR ( dbuf ) , MIO_BECS_CPTR ( dbuf , val_offset ) , 1 ) ;
}
return 0 ;
}
2020-05-20 10:25:12 +00:00
static int cgi_peer_on_fork ( mio_dev_pro_t * pro , void * fork_ctx )
{
2020-05-24 02:48:51 +00:00
mio_t * mio = pro - > mio ; /* in this callback, the pro device is not fully up. however, the mio field is guaranteed to be available */
2020-05-20 10:25:12 +00:00
cgi_peer_fork_ctx_t * fc = ( cgi_peer_fork_ctx_t * ) fork_ctx ;
mio_oow_t content_length ;
const mio_bch_t * qparam ;
2020-05-24 07:13:42 +00:00
mio_bch_t * path , * lang ;
2020-05-20 14:53:05 +00:00
mio_bch_t tmp [ 256 ] ;
mio_becs_t dbuf ;
2020-05-20 10:25:12 +00:00
qparam = mio_htre_getqparam ( fc - > req ) ;
2020-05-24 02:48:51 +00:00
path = mio_dupbcstr ( mio , getenv ( " PATH " ) , MIO_NULL ) ;
lang = mio_dupbcstr ( mio , getenv ( " LANG " ) , MIO_NULL ) ;
2020-05-23 06:44:19 +00:00
# if defined(HAVE_CLEARENV)
2020-05-20 10:25:12 +00:00
clearenv ( ) ;
2020-05-23 06:44:19 +00:00
# else
2020-05-24 02:35:13 +00:00
{
extern char * * environ ;
2020-05-24 02:48:51 +00:00
/* environ = NULL; this crashed this program on NetBSD */
2020-05-24 02:35:13 +00:00
if ( environ ) environ [ 0 ] = ' \0 ' ;
}
2020-05-23 06:44:19 +00:00
# endif
2020-05-24 02:48:51 +00:00
if ( path )
{
setenv ( " PATH " , path , 1 ) ;
mio_freemem ( mio , path ) ;
}
if ( lang )
{
setenv ( " LANG " , lang , 1 ) ;
mio_freemem ( mio , lang ) ;
}
2020-05-20 10:25:12 +00:00
setenv ( " GATEWAY_INTERFACE " , " CGI/1.1 " , 1 ) ;
2020-05-24 02:48:51 +00:00
mio_fmttobcstr ( mio , tmp , MIO_COUNTOF ( tmp ) , " HTTP/%d.%d " , ( int ) mio_htre_getmajorversion ( fc - > req ) , ( int ) mio_htre_getminorversion ( fc - > req ) ) ;
2020-05-20 10:25:12 +00:00
setenv ( " SERVER_PROTOCOL " , tmp , 1 ) ;
setenv ( " DOCUMENT_ROOT " , fc - > docroot , 1 ) ;
2020-05-20 16:14:36 +00:00
setenv ( " SCRIPT_NAME " , fc - > script , 1 ) ;
setenv ( " SCRIPT_FILENAME " , fc - > actual_script , 1 ) ;
/* TODO: PATH_INFO */
2020-05-20 10:25:12 +00:00
setenv ( " REQUEST_METHOD " , mio_htre_getqmethodname ( fc - > req ) , 1 ) ;
setenv ( " REQUEST_URI " , mio_htre_getqpath ( fc - > req ) , 1 ) ;
if ( qparam ) setenv ( " QUERY_STRING " , qparam , 1 ) ;
if ( get_request_content_length ( fc - > req , & content_length ) = = 0 )
{
mio_fmt_uintmax_to_bcstr ( tmp , MIO_COUNTOF ( tmp ) , content_length , 10 , 0 , ' \0 ' , MIO_NULL ) ;
setenv ( " CONTENT_LENGTH " , tmp , 1 ) ;
}
else
{
/* content length unknown, neither is it 0 - this is not standard */
setenv ( " CONTENT_LENGTH " , " -1 " , 1 ) ;
}
setenv ( " SERVER_SOFTWARE " , fc - > cli - > htts - > server_name , 1 ) ;
2020-05-24 02:48:51 +00:00
mio_skadtobcstr ( mio , & fc - > cli - > sck - > localaddr , tmp , MIO_COUNTOF ( tmp ) , MIO_SKAD_TO_BCSTR_ADDR ) ;
2020-05-20 14:53:05 +00:00
setenv ( " SERVER_ADDR " , tmp , 1 ) ;
gethostname ( tmp , MIO_COUNTOF ( tmp ) ) ; /* if this fails, i assume tmp contains the ip address set by mio_skadtobcstr() above */
setenv ( " SERVER_NAME " , tmp , 1 ) ;
2020-05-24 02:48:51 +00:00
mio_skadtobcstr ( mio , & fc - > cli - > sck - > localaddr , tmp , MIO_COUNTOF ( tmp ) , MIO_SKAD_TO_BCSTR_PORT ) ;
2020-05-20 14:53:05 +00:00
setenv ( " SERVER_PORT " , tmp , 1 ) ;
2020-05-20 10:25:12 +00:00
2020-05-24 02:48:51 +00:00
mio_skadtobcstr ( mio , & fc - > cli - > sck - > remoteaddr , tmp , MIO_COUNTOF ( tmp ) , MIO_SKAD_TO_BCSTR_ADDR ) ;
2020-05-20 14:53:05 +00:00
setenv ( " REMOTE_ADDR " , tmp , 1 ) ;
2020-05-24 02:48:51 +00:00
mio_skadtobcstr ( mio , & fc - > cli - > sck - > remoteaddr , tmp , MIO_COUNTOF ( tmp ) , MIO_SKAD_TO_BCSTR_PORT ) ;
2020-05-20 14:53:05 +00:00
setenv ( " REMOTE_PORT " , tmp , 1 ) ;
2020-05-24 02:48:51 +00:00
if ( mio_becs_init ( & dbuf , mio , 256 ) > = 0 )
2020-05-20 14:53:05 +00:00
{
mio_htre_walkheaders ( fc - > req , cgi_peer_capture_request_header , & dbuf ) ;
/* [NOTE] trailers are not available when this cgi resource is started. let's not call mio_htre_walktrailers() */
mio_becs_fini ( & dbuf ) ;
}
2020-05-20 10:25:12 +00:00
return 0 ;
}
2020-05-20 16:14:36 +00:00
int mio_svc_htts_docgi ( mio_svc_htts_t * htts , mio_dev_sck_t * csck , mio_htre_t * req , const mio_bch_t * docroot , const mio_bch_t * script )
2020-05-12 11:46:00 +00:00
{
2020-05-15 06:18:49 +00:00
mio_t * mio = htts - > mio ;
mio_svc_htts_cli_t * cli = mio_dev_sck_getxtn ( csck ) ;
cgi_state_t * cgi_state = MIO_NULL ;
cgi_peer_xtn_t * cgi_peer ;
2020-05-12 11:46:00 +00:00
mio_dev_pro_make_t mi ;
2020-05-20 10:25:12 +00:00
cgi_peer_fork_ctx_t fc ;
2020-05-12 11:46:00 +00:00
2020-05-18 08:59:25 +00:00
/* ensure that you call this function before any contents is received */
MIO_ASSERT ( mio , mio_htre_getcontentlen ( req ) = = 0 ) ;
2020-05-15 06:18:49 +00:00
2020-05-20 10:25:12 +00:00
MIO_MEMSET ( & fc , 0 , MIO_SIZEOF ( fc ) ) ;
fc . cli = cli ;
fc . req = req ;
fc . docroot = docroot ;
2020-05-20 16:14:36 +00:00
fc . script = script ;
fc . actual_script = mio_svc_htts_dupmergepaths ( htts , docroot , script ) ;
if ( ! fc . actual_script ) goto oops ;
2020-05-20 10:25:12 +00:00
2020-05-12 11:46:00 +00:00
MIO_MEMSET ( & mi , 0 , MIO_SIZEOF ( mi ) ) ;
2020-05-15 06:18:49 +00:00
mi . flags = MIO_DEV_PRO_READOUT | MIO_DEV_PRO_ERRTONUL | MIO_DEV_PRO_WRITEIN /*| MIO_DEV_PRO_FORGET_CHILD*/ ;
2020-05-20 16:14:36 +00:00
mi . cmd = fc . actual_script ;
2020-05-12 11:46:00 +00:00
mi . on_read = cgi_peer_on_read ;
mi . on_write = cgi_peer_on_write ;
mi . on_close = cgi_peer_on_close ;
2020-05-20 10:25:12 +00:00
mi . on_fork = cgi_peer_on_fork ;
mi . fork_ctx = & fc ;
2020-05-12 11:46:00 +00:00
2020-05-18 16:40:00 +00:00
cgi_state = ( cgi_state_t * ) mio_svc_htts_rsrc_make ( htts , MIO_SIZEOF ( * cgi_state ) , cgi_state_on_kill ) ;
2020-05-15 06:18:49 +00:00
if ( MIO_UNLIKELY ( ! cgi_state ) ) goto oops ;
2020-05-12 11:46:00 +00:00
2020-05-19 09:11:39 +00:00
cgi_state - > client = cli ;
2020-05-15 06:18:49 +00:00
/*cgi_state->num_pending_writes_to_client = 0;
cgi_state - > num_pending_writes_to_peer = 0 ; */
2020-05-16 19:12:10 +00:00
cgi_state - > req_version = * mio_htre_getversion ( req ) ;
2020-05-18 09:12:36 +00:00
cgi_state - > req_content_length_unlimited = get_request_content_length ( req , & cgi_state - > req_content_length ) ;
2020-05-12 11:46:00 +00:00
2020-05-19 09:11:39 +00:00
cgi_state - > client_org_on_read = csck - > on_read ;
cgi_state - > client_org_on_write = csck - > on_write ;
2020-05-20 10:25:12 +00:00
cgi_state - > client_org_on_disconnect = csck - > on_disconnect ;
2020-05-12 11:46:00 +00:00
csck - > on_read = cgi_client_on_read ;
2020-05-15 06:18:49 +00:00
csck - > on_write = cgi_client_on_write ;
2020-05-20 10:25:12 +00:00
csck - > on_disconnect = cgi_client_on_disconnect ;
2020-05-12 11:46:00 +00:00
2020-05-18 16:40:00 +00:00
MIO_ASSERT ( mio , cli - > rsrc = = MIO_NULL ) ;
2020-05-20 10:25:12 +00:00
MIO_SVC_HTTS_RSRC_ATTACH ( cgi_state , cli - > rsrc ) ;
2020-05-12 11:46:00 +00:00
2020-05-19 18:07:28 +00:00
if ( access ( mi . cmd , X_OK ) = = - 1 )
{
2020-05-24 07:13:42 +00:00
cgi_state_send_final_status_to_client ( cgi_state , 403 , 1 ) ; /* 403 Forbidden */
2020-05-19 18:07:28 +00:00
goto oops ; /* TODO: must not go to oops. just destroy the cgi_state and finalize the request .. */
}
2020-05-15 06:18:49 +00:00
cgi_state - > peer = mio_dev_pro_make ( mio , MIO_SIZEOF ( * cgi_peer ) , & mi ) ;
if ( MIO_UNLIKELY ( ! cgi_state - > peer ) ) goto oops ;
cgi_peer = mio_dev_pro_getxtn ( cgi_state - > peer ) ;
2020-05-20 10:25:12 +00:00
MIO_SVC_HTTS_RSRC_ATTACH ( cgi_state , cgi_peer - > state ) ;
2020-05-15 06:18:49 +00:00
cgi_state - > peer_htrd = mio_htrd_open ( mio , MIO_SIZEOF ( * cgi_peer ) ) ;
if ( MIO_UNLIKELY ( ! cgi_state - > peer_htrd ) ) goto oops ;
2020-05-16 19:12:10 +00:00
mio_htrd_setopt ( cgi_state - > peer_htrd , MIO_HTRD_SKIPINITIALLINE | MIO_HTRD_RESPONSE ) ;
mio_htrd_setrecbs ( cgi_state - > peer_htrd , & cgi_peer_htrd_recbs ) ;
2020-05-15 06:18:49 +00:00
cgi_peer = mio_htrd_getxtn ( cgi_state - > peer_htrd ) ;
2020-05-20 10:25:12 +00:00
MIO_SVC_HTTS_RSRC_ATTACH ( cgi_state , cgi_peer - > state ) ;
2020-05-18 08:59:25 +00:00
# if !defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH)
if ( cgi_state - > req_content_length_unlimited )
{
/* Transfer-Encoding is chunked. no content-length is known in advance. */
/* option 1. buffer contents. if it gets too large, send 413 Request Entity Too Large.
* option 2. send 411 Length Required immediately
* option 3. set Content - Length to - 1 and use EOF to indicate the end of content [ Non - Standard ] */
2020-05-24 07:13:42 +00:00
if ( cgi_state_send_final_status_to_client ( cgi_state , 411 , 1 ) < = - 1 ) goto oops ;
2020-05-18 08:59:25 +00:00
}
# endif
2020-05-15 06:18:49 +00:00
if ( req - > flags & MIO_HTRE_ATTR_EXPECT100 )
{
/* TODO: Expect: 100-continue? who should handle this? cgi? or the http server? */
2020-05-18 08:59:25 +00:00
/* CAN I LET the cgi SCRIPT handle this? */
if ( mio_comp_http_version_numbers ( & req - > version , 1 , 1 ) > = 0 & &
( cgi_state - > req_content_length_unlimited | | cgi_state - > req_content_length > 0 ) )
2020-05-15 06:18:49 +00:00
{
/*
* Don ' t send 100 Continue if http verions is lower than 1.1
* [ RFC7231 ]
* A server that receives a 100 - continue expectation in an HTTP / 1.0
* request MUST ignore that expectation .
*
* Don ' t send 100 Continue if expected content lenth is 0.
* [ RFC7231 ]
* A server MAY omit sending a 100 ( Continue ) response if it has
* already received some or all of the message body for the
* corresponding request , or if the framing indicates that there is
* no message body .
*/
2020-05-17 18:09:19 +00:00
mio_bch_t msgbuf [ 64 ] ;
mio_oow_t msglen ;
msglen = mio_fmttobcstr ( mio , msgbuf , MIO_COUNTOF ( msgbuf ) , " HTTP/%d.%d 100 Continue \r \n \r \n " , cgi_state - > req_version . major , cgi_state - > req_version . minor ) ;
2020-05-18 08:59:25 +00:00
if ( cgi_state_write_to_client ( cgi_state , msgbuf , msglen ) < = - 1 ) goto oops ;
2020-05-19 18:00:49 +00:00
cgi_state - > ever_attempted_to_write_to_client = 0 ; /* reset this as it's polluted for 100 continue */
2020-05-15 06:18:49 +00:00
}
}
else if ( req - > flags & MIO_HTRE_ATTR_EXPECT )
{
2020-05-18 08:59:25 +00:00
/* 417 Expectation Failed */
2020-05-24 07:13:42 +00:00
cgi_state_send_final_status_to_client ( cgi_state , 417 , 1 ) ;
2020-05-18 08:59:25 +00:00
goto oops ;
2020-05-15 06:18:49 +00:00
}
2020-05-18 08:59:25 +00:00
# if defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH)
if ( cgi_state - > req_content_length_unlimited )
2020-05-15 06:18:49 +00:00
{
2020-05-18 08:59:25 +00:00
/* Transfer-Encoding is chunked. no content-length is known in advance. */
/* option 1. buffer contents. if it gets too large, send 413 Request Entity Too Large.
* option 2. send 411 Length Required immediately
* option 3. set Content - Length to - 1 and use EOF to indicate the end of content [ Non - Standard ] */
2020-05-12 11:46:00 +00:00
2020-05-18 08:59:25 +00:00
/* TODO: must set CONTENT-LENGTH to -1 before start the process */
/* change the callbacks to subscribe to contents to be uploaded */
2020-05-19 09:11:39 +00:00
cgi_state - > client - > htrd - > recbs . poke = cgi_client_htrd_poke ;
cgi_state - > client - > htrd - > recbs . push_content = cgi_client_htrd_push_content ;
2020-05-15 06:18:49 +00:00
}
2020-05-17 18:09:19 +00:00
else
{
2020-05-18 08:59:25 +00:00
# endif
if ( cgi_state - > req_content_length > 0 )
2020-05-17 18:09:19 +00:00
{
2020-05-18 08:59:25 +00:00
/* change the callbacks to subscribe to contents to be uploaded */
2020-05-19 09:11:39 +00:00
cgi_state - > client - > htrd - > recbs . poke = cgi_client_htrd_poke ;
cgi_state - > client - > htrd - > recbs . push_content = cgi_client_htrd_push_content ;
2020-05-17 18:09:19 +00:00
}
2020-05-18 08:59:25 +00:00
else
{
/* no content to be uploaded from the client */
/* indicate EOF to the peer and disable input wathching from the client */
if ( cgi_state_write_to_peer ( cgi_state , MIO_NULL , 0 ) < = - 1 ) goto oops ;
2020-05-19 09:11:39 +00:00
cgi_state_mark_over ( cgi_state , CGI_STATE_OVER_READ_FROM_CLIENT | CGI_STATE_OVER_WRITE_TO_PEER ) ;
2020-05-18 08:59:25 +00:00
}
# if defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH)
2020-05-17 18:09:19 +00:00
}
2020-05-18 08:59:25 +00:00
# endif
2020-05-19 09:11:39 +00:00
/* this may change later if Content-Length is included in the cgi output */
if ( req - > flags & MIO_HTRE_ATTR_KEEPALIVE )
{
cgi_state - > keep_alive = 1 ;
cgi_state - > res_mode_to_cli = CGI_STATE_RES_MODE_CHUNKED ;
/* the mode still can get switched to CGI_STATE_RES_MODE_LENGTH if the cgi script emits Content-Length */
}
else
{
cgi_state - > keep_alive = 0 ;
cgi_state - > res_mode_to_cli = CGI_STATE_RES_MODE_CLOSE ;
}
2020-05-18 08:59:25 +00:00
2020-05-19 09:11:39 +00:00
/* TODO: store current input watching state and use it when destroying the cgi_state data */
if ( mio_dev_sck_read ( csck , ! ( cgi_state - > over & CGI_STATE_OVER_READ_FROM_CLIENT ) ) < = - 1 ) goto oops ;
2020-05-20 16:14:36 +00:00
mio_freemem ( mio , fc . actual_script ) ;
2020-05-15 06:18:49 +00:00
return 0 ;
2020-05-12 11:46:00 +00:00
oops :
2020-05-15 06:18:49 +00:00
MIO_DEBUG2 ( mio , " HTTS(%p) - FAILURE in docgi - socket(%p) \n " , htts , csck ) ;
2020-05-18 16:40:00 +00:00
if ( cgi_state ) cgi_state_halt_participating_devices ( cgi_state ) ;
2020-05-20 16:14:36 +00:00
if ( fc . actual_script ) mio_freemem ( mio , fc . actual_script ) ;
2020-05-12 11:46:00 +00:00
return - 1 ;
}
/* ----------------------------------------------------------------- */
2020-05-21 07:30:14 +00:00
struct thr_state_t
{
MIO_SVC_HTTS_RSRC_HEADER ;
pthread_t thr ;
mio_svc_htts_thr_func_t func ;
mio_dev_t * p1 ; /* change it to pipe */
} ;
typedef struct thr_state_t thr_state_t ;
static void thr_state_on_kill ( thr_state_t * thr_state )
2020-05-20 16:14:36 +00:00
{
}
2020-05-21 07:30:14 +00:00
static void * thr_state_run_func ( void * arg )
{
thr_state_t * thr_state = ( thr_state_t * ) arg ;
// thr_state->func (thr_state->XXX, thr_state->p1->fd); /* pass the pointer to data that can be accessed safely inside a thread. mostly these must be duplicates of core data */
return MIO_NULL ;
}
int mio_svc_htts_dothr ( mio_svc_htts_t * htts , mio_dev_sck_t * csck , mio_htre_t * req , mio_svc_htts_thr_func_t func )
{
2020-05-24 02:48:51 +00:00
/*mio_t* mio = htts->mio;*/
2020-05-21 07:30:14 +00:00
// mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck);
thr_state_t * thr_state = MIO_NULL ;
thr_state = ( thr_state_t * ) mio_svc_htts_rsrc_make ( htts , MIO_SIZEOF ( * thr_state ) , thr_state_on_kill ) ;
if ( MIO_UNLIKELY ( ! thr_state ) ) goto oops ;
thr_state - > func = func ;
#if 0
thr_state - > p1 = mio_dev_pipe_make ( mio ) ;
if ( MIO_UNLIKELY ( ! thr_state - > p1 ) ) goto oops ;
2020-05-20 16:14:36 +00:00
# endif
2020-05-21 07:30:14 +00:00
if ( pthread_create ( & thr_state - > thr , MIO_NULL , thr_state_run_func , thr_state ) ! = 0 )
{
}
return 0 ;
oops :
//if (thr_state) thr_state_halt_particiapting_devices (thr_state);
return - 1 ;
}
2020-05-20 16:14:36 +00:00
/* ----------------------------------------------------------------- */
int mio_svc_htts_dofile ( mio_svc_htts_t * htts , mio_dev_sck_t * csck , mio_htre_t * req , const mio_bch_t * docroot , const mio_bch_t * file )
2020-05-16 19:12:10 +00:00
{
switch ( mio_htre_getqmethodtype ( req ) )
{
case MIO_HTTP_OPTIONS :
mio_htre_discardcontent ( req ) ;
/* TODO: return 200 with Allow: OPTIONS, HEAD, GET, POST, PUT, DELETE... */
/* However, if there is access configuration, the item list must reflect it */
break ;
case MIO_HTTP_HEAD :
case MIO_HTTP_GET :
case MIO_HTTP_POST :
mio_htre_discardcontent ( req ) ;
break ;
case MIO_HTTP_PUT :
break ;
case MIO_HTTP_DELETE :
mio_htre_discardcontent ( req ) ;
break ;
default :
/* method not allowed */
mio_htre_discardcontent ( req ) ;
/* schedule to send 405 */
break ;
}
return 0 ;
}
/* ----------------------------------------------------------------- */
2020-05-12 11:46:00 +00:00
int mio_svc_htts_doproxy ( mio_svc_htts_t * htts , mio_dev_sck_t * csck , mio_htre_t * req , const mio_bch_t * upstream )
2020-05-06 09:28:36 +00:00
{
2020-05-12 11:46:00 +00:00
#if 0
2020-05-08 09:48:26 +00:00
1. attempt to connect to the proxy target . . .
2. in the mean time ,
mio_dev_watch ( csck , MIO_DEV_WATCH_UPDATE , 0 ) ; /* no input, no output watching */
3. once connected ,
mio_dev_watch ( csck , MIO_DEV_WATCH_RENEW , MIO_DEV_EVENT_IN ) ; /* enable input watching. if needed, enable output watching */
4. start proxying
2020-05-10 16:20:39 +00:00
5. if one side is stalled , donot read from another side . . . let the kernel slow the connection . . .
2020-05-08 09:48:26 +00:00
i need to know how may bytes are pending for this .
if pending too high , disable read watching . . . mio_dev_watch ( csck , MIO_DEV_WATCH_RENEW , 0 ) ;
# endif
2020-05-12 11:46:00 +00:00
return 0 ;
}
2020-05-18 16:40:00 +00:00
/* ----------------------------------------------------------------- */
void mio_svc_htts_fmtgmtime ( mio_svc_htts_t * htts , const mio_ntime_t * nt , mio_bch_t * buf , mio_oow_t len )
{
mio_ntime_t now ;
if ( ! nt )
{
mio_sys_getrealtime ( htts - > mio , & now ) ;
nt = & now ;
}
mio_fmt_http_time_to_bcstr ( nt , buf , len ) ;
}
2020-05-08 09:48:26 +00:00
2020-05-20 16:14:36 +00:00
mio_bch_t * mio_svc_htts_dupmergepaths ( mio_svc_htts_t * htts , const mio_bch_t * base , const mio_bch_t * path )
{
mio_bch_t * xpath ;
const mio_bch_t * ta [ 4 ] ;
mio_oow_t idx = 0 ;
ta [ idx + + ] = base ;
if ( path [ 0 ] ! = ' \0 ' )
{
ta [ idx + + ] = " / " ;
ta [ idx + + ] = path ;
}
ta [ idx + + ] = MIO_NULL ;
xpath = mio_dupbcstrs ( htts - > mio , ta , MIO_NULL ) ;
if ( MIO_UNLIKELY ( ! xpath ) ) return MIO_NULL ;
mio_canon_bcstr_path ( xpath , xpath , 0 ) ;
return xpath ;
}
2020-05-10 16:20:39 +00:00
/* ----------------------------------------------------------------- */
2020-05-18 16:40:00 +00:00
#if 0
2020-05-16 19:12:10 +00:00
/* ----------------------------------------------------------------- */
int mio_svc_htts_sendrsrc ( mio_svc_htts_t * htts , mio_dev_sck_t * csck , mio_svc_htts_rsrc_t * rsrc , int status_code , mio_http_method_t method , const mio_http_version_t * version , int keepalive )
{
mio_svc_htts_cli_t * cli = mio_dev_sck_getxtn ( csck ) ;
mio_bch_t dtbuf [ 64 ] ;
mio_oow_t x ;
#if 0
if ( ( req - > flags & MIO_HTRE_ATTR_EXPECT ) & &
mio_comp_http_version_numbers ( & req - > version , 1 , 1 ) > = 0 & &
mio_htre_getcontentlen ( req ) < = 0 )
{
if ( req - > flags & MIO_HTRE_ATTR_EXPECT100 )
{
mio_dev_sck_write ( csck , " HTTP/1.1 100 Continue \r \n " , 23 , MIO_NULL , MIO_NULL ) ;
}
else
{
}
}
# endif
mio_svc_htts_fmtgmtime ( htts , MIO_NULL , dtbuf , MIO_COUNTOF ( dtbuf ) ) ;
x = mio_becs_fmt ( cli - > sbuf , " HTTP/%d.%d %d %hs \r \n Server: %hs \r \n Date: %hs \r \n Connection: %s \r \n " ,
( int ) version - > major , ( int ) version - > minor ,
( int ) status_code , mio_http_status_to_bcstr ( status_code ) ,
htts - > server_name ,
dtbuf , /* Date */
( keepalive ? " keep-alive " : " close " ) /* Connection */
) ;
if ( MIO_UNLIKELY ( x = = ( mio_oow_t ) - 1 ) ) return - 1 ;
if ( rsrc - > content_type )
{
x = mio_becs_fcat ( cli - > sbuf , " Content-Type: %hs \r \n " , rsrc - > content_type ) ;
if ( MIO_UNLIKELY ( x = = ( mio_oow_t ) - 1 ) ) return - 1 ;
}
if ( rsrc - > flags & MIO_SVC_HTTS_RSRC_FLAG_CONTENT_LENGTH_SET )
{
x = mio_becs_fcat ( cli - > sbuf , " Content-Length: %ju \r \n " , ( mio_uintmax_t ) rsrc - > content_length ) ;
if ( MIO_UNLIKELY ( x = = ( mio_oow_t ) - 1 ) ) return - 1 ;
}
x = mio_becs_fcat ( cli - > sbuf , " \r \n " , rsrc - > content_length ) ;
if ( MIO_UNLIKELY ( x = = ( mio_oow_t ) - 1 ) ) return - 1 ;
/* TODO: handle METHOD HEAD... should abort after header trasmission... */
/* TODO: allow extra header items */
/* TODO: use timedwrite? */
if ( mio_dev_sck_write ( csck , MIO_BECS_PTR ( cli - > sbuf ) , MIO_BECS_LEN ( cli - > sbuf ) , rsrc , MIO_NULL ) < = - 1 )
{
mio_dev_sck_halt ( csck ) ;
return - 1 ;
}
return 0 ;
}
/* ----------------------------------------------------------------- */
struct rsrc_file_xtn_t
{
int fd ;
mio_foff_t range_offset ;
mio_foff_t range_start ;
mio_foff_t range_end ;
mio_bch_t rbuf [ 4096 ] ;
} ;
typedef struct rsrc_file_xtn_t rsrc_file_xtn_t ;
static int rsrc_file_on_write ( mio_svc_htts_rsrc_t * rsrc , mio_dev_sck_t * sck )
{
rsrc_file_xtn_t * file = ( rsrc_file_xtn_t * ) mio_svc_htts_rsrc_getxtn ( rsrc ) ;
ssize_t n ;
if ( file - > range_offset > = file - > range_end ) return 0 ; /* transmitted as long as content-length. end of the resource */
if ( file - > range_offset < file - > range_start )
{
if ( lseek ( file - > fd , SEEK_SET , file - > range_start ) = = ( off_t ) - 1 ) return - 1 ;
file - > range_offset = file - > range_start ;
}
n = read ( file - > fd , file - > rbuf , MIO_SIZEOF ( file - > rbuf ) ) ;
if ( n < = 0 ) return - 1 ;
if ( n > file - > range_end - file - > range_start )
{
/* TODO: adjust */
n = file - > range_end - file - > range_start ;
}
file - > range_offset + = n ;
if ( mio_dev_sck_write ( sck , file - > rbuf , n , rsrc , MIO_NULL ) < = - 1 ) return - 1 ; /* failure */
return 1 ; /* keep calling me */
}
static void rsrc_file_on_kill ( mio_svc_htts_rsrc_t * rsrc )
{
rsrc_file_xtn_t * file = ( rsrc_file_xtn_t * ) mio_svc_htts_rsrc_getxtn ( rsrc ) ;
if ( file - > fd > = 0 )
{
close ( file - > fd ) ;
file - > fd = - 1 ;
}
}
int mio_svc_htts_sendfile ( mio_svc_htts_t * htts , mio_dev_sck_t * csck , const mio_bch_t * file_path , int status_code , mio_http_method_t method , const mio_http_version_t * version , int keepalive )
{
//mio_svc_htts_cli_t* cli = (mio_svc_htts_cli_t*)mio_dev_sck_getxtn(csck);
int fd ;
struct stat st ;
mio_svc_htts_rsrc_t * rsrc = MIO_NULL ;
rsrc_file_xtn_t * rsrc_file ;
fd = open ( file_path , O_RDONLY , 0 ) ;
if ( fd < = - 1 )
{
/* TODO: divert to writing status code not found... */
goto oops ; /* write error status instead */
}
if ( fstat ( fd , & st ) < = - 1 ) goto oops ; /* TODO: write error status instead. probably 500 internal server error???*/
rsrc = mio_svc_htts_rsrc_make ( htts , csck , rsrc_file_on_write , rsrc_file_on_kill , MIO_SIZEOF ( * rsrc_file ) ) ;
if ( MIO_UNLIKELY ( ! rsrc ) ) goto oops ;
rsrc - > flags = MIO_SVC_HTTS_RSRC_FLAG_CONTENT_LENGTH_SET ;
rsrc - > content_length = st . st_size ;
rsrc - > content_type = " text/plain " ;
rsrc_file = mio_svc_htts_rsrc_getxtn ( rsrc ) ;
rsrc_file - > fd = fd ;
rsrc_file - > range_start = 0 ;
rsrc_file - > range_end = st . st_size ;
if ( mio_svc_htts_sendrsrc ( htts , csck , rsrc , 200 , method , version , keepalive ) < = - 1 ) goto oops ;
return 0 ;
oops :
if ( rsrc ) mio_svc_htts_rsrc_kill ( rsrc ) ;
if ( fd > = 0 ) close ( fd ) ;
return - 1 ;
}
# endif