2020-07-08 05:24:40 +00:00
/*
Copyright ( c ) 2016 - 2020 Chung , Hyung - Hwan . All rights reserved .
Redistribution and use in source and binary forms , with or without
modification , are permitted provided that the following conditions
are met :
1. Redistributions of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
2. Redistributions in binary form must reproduce the above copyright
notice , this list of conditions and the following disclaimer in the
documentation and / or other materials provided with the distribution .
THIS SOFTWARE IS PROVIDED BY THE AUTHOR " AS IS " AND ANY EXPRESS OR
2022-06-11 05:32:01 +00:00
IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
2020-07-08 05:24:40 +00:00
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include "http-prv.h"
2021-07-22 07:30:20 +00:00
# include <hio-pro.h>
# include <hio-fmt.h>
# include <hio-chr.h>
2020-07-08 05:24:40 +00:00
# include <unistd.h> /* TODO: move file operations to sys-file.XXX */
# include <fcntl.h>
# include <sys/stat.h>
2020-07-09 09:54:13 +00:00
# include <errno.h>
2020-11-15 04:02:47 +00:00
# include <netinet/in.h>
2020-07-20 14:08:25 +00:00
# include <netinet/tcp.h>
2020-07-08 05:24:40 +00:00
# define FILE_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH
2020-11-16 16:52:49 +00:00
enum file_res_mode_t
2020-07-08 05:24:40 +00:00
{
2020-11-16 16:52:49 +00:00
FILE_RES_MODE_CLOSE ,
FILE_RES_MODE_LENGTH
2020-07-08 05:24:40 +00:00
} ;
2020-11-16 16:52:49 +00:00
typedef enum file_res_mode_t file_res_mode_t ;
2020-07-08 05:24:40 +00:00
2020-11-16 16:52:49 +00:00
# define FILE_OVER_READ_FROM_CLIENT (1 << 0)
# define FILE_OVER_READ_FROM_PEER (1 << 1)
# define FILE_OVER_WRITE_TO_CLIENT (1 << 2)
# define FILE_OVER_WRITE_TO_PEER (1 << 3)
# define FILE_OVER_ALL (FILE_OVER_READ_FROM_CLIENT | FILE_OVER_READ_FROM_PEER | FILE_OVER_WRITE_TO_CLIENT | FILE_OVER_WRITE_TO_PEER)
2020-07-08 05:24:40 +00:00
2020-11-16 16:52:49 +00:00
struct file_t
2020-07-08 05:24:40 +00:00
{
2021-07-22 07:30:20 +00:00
HIO_SVC_HTTS_RSRC_HEADER ;
2020-07-08 05:24:40 +00:00
2022-10-09 16:41:07 +00:00
int options ;
2022-12-21 14:57:52 +00:00
hio_svc_htts_file_cbs_t * cbs ;
2021-07-22 07:30:20 +00:00
hio_oow_t num_pending_writes_to_client ;
hio_oow_t num_pending_writes_to_peer ;
2020-07-19 18:55:35 +00:00
int sendfile_ok ;
2020-07-08 05:24:40 +00:00
int peer ;
2021-07-22 07:30:20 +00:00
hio_foff_t total_size ;
hio_foff_t start_offset ;
hio_foff_t end_offset ;
hio_foff_t cur_offset ;
hio_bch_t peer_buf [ 8192 ] ;
hio_tmridx_t peer_tmridx ;
hio_bch_t peer_etag [ 128 ] ;
hio_svc_htts_cli_t * client ;
hio_http_version_t req_version ; /* client request */
hio_http_method_t req_method ;
2022-12-26 17:03:53 +00:00
hio_bch_t * req_qpath ;
2020-07-08 05:24:40 +00:00
2020-11-16 16:52:49 +00:00
unsigned int over : 4 ; /* must be large enough to accomodate FILE_OVER_ALL */
2020-07-08 05:24:40 +00:00
unsigned int keep_alive : 1 ;
unsigned int req_content_length_unlimited : 1 ;
2022-12-26 17:03:53 +00:00
unsigned int req_qpath_ending_with_slash : 1 ;
2022-12-28 07:24:00 +00:00
unsigned int req_qpath_is_root : 1 ;
2020-07-08 05:24:40 +00:00
unsigned int ever_attempted_to_write_to_client : 1 ;
2022-10-08 07:47:55 +00:00
unsigned int client_eof_detected : 1 ;
2020-07-08 05:24:40 +00:00
unsigned int client_disconnected : 1 ;
unsigned int client_htrd_recbs_changed : 1 ;
2020-07-20 14:08:25 +00:00
unsigned int etag_match : 1 ;
2021-07-22 07:30:20 +00:00
hio_oow_t req_content_length ; /* client request content length */
2020-11-16 16:52:49 +00:00
file_res_mode_t res_mode_to_cli ;
2020-07-08 05:24:40 +00:00
2021-07-22 07:30:20 +00:00
hio_dev_sck_on_read_t client_org_on_read ;
hio_dev_sck_on_write_t client_org_on_write ;
hio_dev_sck_on_disconnect_t client_org_on_disconnect ;
hio_htrd_recbs_t client_htrd_org_recbs ;
2020-07-08 05:24:40 +00:00
} ;
2020-11-16 16:52:49 +00:00
typedef struct file_t file_t ;
2020-07-08 05:24:40 +00:00
2020-11-16 16:52:49 +00:00
static int file_send_contents_to_client ( file_t * file ) ;
2020-07-13 10:01:42 +00:00
2020-11-16 16:52:49 +00:00
static void file_halt_participating_devices ( file_t * file )
2020-07-08 05:24:40 +00:00
{
2021-07-22 07:30:20 +00:00
HIO_ASSERT ( file - > client - > htts - > hio , file - > client ! = HIO_NULL ) ;
HIO_ASSERT ( file - > client - > htts - > hio , file - > client - > sck ! = HIO_NULL ) ;
2020-07-08 05:24:40 +00:00
2022-10-09 16:41:07 +00:00
HIO_DEBUG3 ( file - > client - > htts - > hio , " HTTS(%p) - file(c=%d,p=%d) Halting participating devices \n " , file - > client - > htts , ( int ) file - > client - > sck - > hnd , ( int ) file - > peer ) ;
2020-07-08 05:24:40 +00:00
2022-10-08 07:47:55 +00:00
/* only the client socket device.
* the peer side is just a file descriptor - no hio - managed device */
2021-07-22 07:30:20 +00:00
hio_dev_sck_halt ( file - > client - > sck ) ;
2020-07-08 05:24:40 +00:00
}
2021-07-22 07:30:20 +00:00
static int file_write_to_client ( file_t * file , const void * data , hio_iolen_t dlen )
2020-07-08 05:24:40 +00:00
{
2020-11-16 16:52:49 +00:00
file - > ever_attempted_to_write_to_client = 1 ;
2020-07-08 05:24:40 +00:00
2020-11-16 16:52:49 +00:00
file - > num_pending_writes_to_client + + ;
2021-07-22 07:30:20 +00:00
if ( hio_dev_sck_write ( file - > client - > sck , data , dlen , HIO_NULL , HIO_NULL ) < = - 1 ) /* TODO: use sendfile here.. */
2020-07-08 05:24:40 +00:00
{
2020-11-16 16:52:49 +00:00
file - > num_pending_writes_to_client - - ;
2020-07-08 05:24:40 +00:00
return - 1 ;
}
return 0 ;
}
2021-07-22 07:30:20 +00:00
static int file_sendfile_to_client ( file_t * file , hio_foff_t foff , hio_iolen_t len )
2020-07-19 06:23:43 +00:00
{
2020-11-16 16:52:49 +00:00
file - > ever_attempted_to_write_to_client = 1 ;
2020-07-19 06:23:43 +00:00
2020-11-16 16:52:49 +00:00
file - > num_pending_writes_to_client + + ;
2021-07-22 07:30:20 +00:00
if ( hio_dev_sck_sendfile ( file - > client - > sck , file - > peer , foff , len , HIO_NULL ) < = - 1 )
2020-07-19 06:23:43 +00:00
{
2020-11-16 16:52:49 +00:00
file - > num_pending_writes_to_client - - ;
2020-07-19 06:23:43 +00:00
return - 1 ;
}
return 0 ;
}
2022-12-26 17:03:53 +00:00
static int file_send_final_status_to_client ( file_t * file , int status_code , int force_close , int dir_redirect )
2020-07-08 05:24:40 +00:00
{
2021-07-22 07:30:20 +00:00
hio_svc_htts_cli_t * cli = file - > client ;
hio_bch_t dtbuf [ 64 ] ;
2022-10-07 05:08:40 +00:00
const hio_bch_t * status_msg ;
2020-07-08 05:24:40 +00:00
2021-07-22 07:30:20 +00:00
hio_svc_htts_fmtgmtime ( cli - > htts , HIO_NULL , dtbuf , HIO_COUNTOF ( dtbuf ) ) ;
2022-10-07 05:08:40 +00:00
status_msg = hio_http_status_to_bcstr ( status_code ) ;
2020-07-08 05:24:40 +00:00
2020-11-16 16:52:49 +00:00
if ( ! force_close ) force_close = ! file - > keep_alive ;
2022-12-27 15:20:30 +00:00
if ( hio_becs_fmt ( cli - > sbuf , " HTTP/%d.%d %d %hs \r \n Server: %hs \r \n Date: %s \r \n Connection: %hs \r \n " ,
2020-11-16 16:52:49 +00:00
file - > req_version . major , file - > req_version . minor ,
2022-10-07 05:08:40 +00:00
status_code , status_msg ,
2020-07-08 05:24:40 +00:00
cli - > htts - > server_name , dtbuf ,
2022-12-27 15:20:30 +00:00
( force_close ? " close " : " keep-alive " ) ) = = ( hio_oow_t ) - 1 ) return - 1 ;
if ( file - > req_method = = HIO_HTTP_HEAD & & status_code ! = HIO_HTTP_STATUS_OK ) status_msg = " " ;
2022-12-26 17:03:53 +00:00
if ( dir_redirect )
{
/* don't send content body when the status code is 3xx. include the Location header only. */
2022-12-31 15:20:07 +00:00
if ( hio_becs_fcat ( cli - > sbuf , " Content-Type: text/plain \r \n Content-Length: 0 \r \n Location: %hs/ \r \n \r \n " , file - > req_qpath ) = = ( hio_oow_t ) - 1 ) return - 1 ;
2022-12-26 17:03:53 +00:00
}
else
{
2022-12-31 15:20:07 +00:00
if ( hio_becs_fcat ( cli - > sbuf , " Content-Type: text/plain \r \n Content-Length: %zu \r \n \r \n %hs " , hio_count_bcstr ( status_msg ) , status_msg ) = = ( hio_oow_t ) - 1 ) return - 1 ;
2022-12-26 17:03:53 +00:00
}
2020-07-08 05:24:40 +00:00
2021-07-22 07:30:20 +00:00
return ( file_write_to_client ( file , HIO_BECS_PTR ( cli - > sbuf ) , HIO_BECS_LEN ( cli - > sbuf ) ) < = - 1 | |
( force_close & & file_write_to_client ( file , HIO_NULL , 0 ) < = - 1 ) ) ? - 1 : 0 ;
2020-07-08 05:24:40 +00:00
}
2020-11-16 16:52:49 +00:00
static void file_close_peer ( file_t * file )
2020-07-08 05:24:40 +00:00
{
2021-07-22 07:30:20 +00:00
hio_t * hio = file - > htts - > hio ;
2020-07-09 09:54:13 +00:00
2021-07-22 07:30:20 +00:00
if ( file - > peer_tmridx ! = HIO_TMRIDX_INVALID )
2020-07-08 05:24:40 +00:00
{
2021-07-22 07:30:20 +00:00
hio_deltmrjob ( hio , file - > peer_tmridx ) ;
HIO_ASSERT ( hio , file - > peer_tmridx = = HIO_TMRIDX_INVALID ) ;
2020-07-08 05:24:40 +00:00
}
2020-07-09 09:54:13 +00:00
2020-11-16 16:52:49 +00:00
if ( file - > peer > = 0 )
2020-07-08 05:24:40 +00:00
{
2020-11-16 16:52:49 +00:00
close ( file - > peer ) ;
file - > peer = - 1 ;
2020-07-08 05:24:40 +00:00
}
}
2020-11-16 16:52:49 +00:00
static void file_mark_over ( file_t * file , int over_bits )
2020-07-08 05:24:40 +00:00
{
unsigned int old_over ;
2020-11-16 16:52:49 +00:00
old_over = file - > over ;
file - > over | = over_bits ;
2020-07-08 05:24:40 +00:00
2022-10-08 07:47:55 +00:00
HIO_DEBUG6 ( file - > htts - > hio , " HTTS(%p) - file(c=%d,p=%d) updating mark - old_over=%x | new-bits=%x => over=%x \n " , file - > htts , ( int ) file - > client - > sck - > hnd , file - > peer , ( int ) old_over , ( int ) over_bits , ( int ) file - > over ) ;
2020-07-08 05:24:40 +00:00
2020-11-16 16:52:49 +00:00
if ( ! ( old_over & FILE_OVER_READ_FROM_CLIENT ) & & ( file - > over & FILE_OVER_READ_FROM_CLIENT ) )
2020-07-08 05:24:40 +00:00
{
2021-07-22 07:30:20 +00:00
if ( hio_dev_sck_read ( file - > client - > sck , 0 ) < = - 1 )
2020-07-08 05:24:40 +00:00
{
2022-10-08 07:47:55 +00:00
HIO_DEBUG3 ( file - > htts - > hio , " HTTS(%p) - file(c=%d,p=%d) halting client for failure to disable input watching \n " , file - > htts , ( int ) file - > client - > sck - > hnd , file - > peer ) ;
2021-07-22 07:30:20 +00:00
hio_dev_sck_halt ( file - > client - > sck ) ;
2020-07-08 05:24:40 +00:00
}
}
2020-07-15 10:30:12 +00:00
#if 0
2020-11-16 16:52:49 +00:00
if ( ! ( old_over & FILE_OVER_READ_FROM_PEER ) & & ( file - > over & FILE_OVER_READ_FROM_PEER ) )
2020-07-08 05:24:40 +00:00
{
2020-07-15 10:30:12 +00:00
/* there is no partial close... keep it open */
2020-07-08 05:24:40 +00:00
}
2020-07-15 10:30:12 +00:00
# endif
2020-07-08 05:24:40 +00:00
2020-11-16 16:52:49 +00:00
if ( old_over ! = FILE_OVER_ALL & & file - > over = = FILE_OVER_ALL )
2020-07-08 05:24:40 +00:00
{
/* ready to stop */
2022-10-08 07:47:55 +00:00
HIO_DEBUG3 ( file - > htts - > hio , " HTTS(%p) - file(c=%d,p=%d) halting peer as it is unneeded \n " , file - > htts , ( int ) file - > client - > sck - > hnd , file - > peer ) ;
2020-11-16 16:52:49 +00:00
file_close_peer ( file ) ;
2020-07-08 05:24:40 +00:00
2022-10-08 07:47:55 +00:00
if ( file - > keep_alive & & ! file - > client_eof_detected )
2020-07-08 05:24:40 +00:00
{
2020-07-20 14:08:25 +00:00
# if defined(TCP_CORK)
int tcp_cork = 0 ;
2020-11-15 04:02:47 +00:00
# if defined(SOL_TCP)
2021-07-22 07:30:20 +00:00
hio_dev_sck_setsockopt ( file - > client - > sck , SOL_TCP , TCP_CORK , & tcp_cork , HIO_SIZEOF ( tcp_cork ) ) ;
2020-11-15 04:02:47 +00:00
# elif defined(IPPROTO_TCP)
2021-07-22 07:30:20 +00:00
hio_dev_sck_setsockopt ( file - > client - > sck , IPPROTO_TCP , TCP_CORK , & tcp_cork , HIO_SIZEOF ( tcp_cork ) ) ;
2020-11-15 04:02:47 +00:00
# endif
2020-07-20 14:08:25 +00:00
# endif
2020-11-16 16:52:49 +00:00
/* how to arrange to delete this file object and put the socket back to the normal waiting state??? */
2021-07-22 07:30:20 +00:00
HIO_ASSERT ( file - > htts - > hio , file - > client - > rsrc = = ( hio_svc_htts_rsrc_t * ) file ) ;
HIO_SVC_HTTS_RSRC_DETACH ( file - > client - > rsrc ) ;
2022-10-08 07:47:55 +00:00
/* the file resource must not be accessed from here down as it could have been destroyed */
2020-07-08 05:24:40 +00:00
}
else
{
2022-10-08 07:47:55 +00:00
HIO_DEBUG4 ( file - > htts - > hio , " HTTS(%p) - file(c=%d,p=%d) halting client for %hs \n " , file - > htts , ( int ) file - > client - > sck - > hnd , file - > peer , ( file - > client_eof_detected ? " EOF detected " : " no keep-alive " ) ) ;
2021-07-22 07:30:20 +00:00
hio_dev_sck_shutdown ( file - > client - > sck , HIO_DEV_SCK_SHUTDOWN_WRITE ) ;
hio_dev_sck_halt ( file - > client - > sck ) ;
2022-10-08 07:47:55 +00:00
/* the file resource will be detached from file->client->rsrc by the upstream disconnect handler in http_svr.c */
2020-07-08 05:24:40 +00:00
}
}
}
2021-07-22 07:30:20 +00:00
static int file_write_to_peer ( file_t * file , const void * data , hio_iolen_t dlen )
2020-07-15 10:30:12 +00:00
{
2021-07-22 07:30:20 +00:00
hio_t * hio = file - > htts - > hio ;
2020-07-15 10:30:12 +00:00
if ( dlen < = 0 )
{
2020-11-16 16:52:49 +00:00
file_mark_over ( file , FILE_OVER_WRITE_TO_PEER ) ;
2020-07-15 10:30:12 +00:00
}
else
{
2022-10-09 16:41:07 +00:00
hio_iolen_t pos , rem , n ;
2021-07-22 07:30:20 +00:00
if ( file - > req_method = = HIO_HTTP_GET ) return 0 ;
2022-10-09 16:41:07 +00:00
if ( file - > peer < = - 1 ) return 0 ; /* peer open proabably failed */
2020-07-15 10:30:12 +00:00
2022-10-09 16:41:07 +00:00
/* TODO: async file io -> create a file device?? */
pos = 0 ;
rem = dlen ;
while ( rem > 0 )
{
n = write ( file - > peer , & ( ( const hio_uint8_t * ) data ) [ pos ] , rem ) ;
if ( n < = - 1 ) return - 1 ;
rem - = n ;
pos + = n ;
}
2020-07-15 10:30:12 +00:00
}
return 0 ;
}
2020-07-08 05:24:40 +00:00
2020-11-16 16:52:49 +00:00
static void file_on_kill ( file_t * file )
2020-07-08 05:24:40 +00:00
{
2021-07-22 07:30:20 +00:00
hio_t * hio = file - > htts - > hio ;
2020-07-08 05:24:40 +00:00
2022-10-08 07:47:55 +00:00
HIO_DEBUG3 ( hio , " HTTS(%p) - file(c=%d,p=%d) on_kill \n " , file - > htts , ( int ) file - > client - > sck - > hnd , file - > peer ) ;
2020-07-08 05:24:40 +00:00
2020-11-16 16:52:49 +00:00
file_close_peer ( file ) ;
2020-07-08 05:24:40 +00:00
2022-12-26 17:03:53 +00:00
if ( file - > req_qpath )
{
hio_freemem ( hio , file - > req_qpath ) ;
file - > req_qpath = HIO_NULL ;
}
2020-11-16 16:52:49 +00:00
if ( file - > client_org_on_read )
2020-07-08 05:24:40 +00:00
{
2020-11-16 16:52:49 +00:00
file - > client - > sck - > on_read = file - > client_org_on_read ;
2021-07-22 07:30:20 +00:00
file - > client_org_on_read = HIO_NULL ;
2020-07-08 05:24:40 +00:00
}
2020-11-16 16:52:49 +00:00
if ( file - > client_org_on_write )
2020-07-08 05:24:40 +00:00
{
2020-11-16 16:52:49 +00:00
file - > client - > sck - > on_write = file - > client_org_on_write ;
2021-07-22 07:30:20 +00:00
file - > client_org_on_write = HIO_NULL ;
2020-07-08 05:24:40 +00:00
}
2020-11-16 16:52:49 +00:00
if ( file - > client_org_on_disconnect )
2020-07-08 05:24:40 +00:00
{
2020-11-16 16:52:49 +00:00
file - > client - > sck - > on_disconnect = file - > client_org_on_disconnect ;
2021-07-22 07:30:20 +00:00
file - > client_org_on_disconnect = HIO_NULL ;
2020-07-08 05:24:40 +00:00
}
2020-11-16 16:52:49 +00:00
if ( file - > client_htrd_recbs_changed )
2020-07-08 05:24:40 +00:00
{
/* restore the callbacks */
2021-07-22 07:30:20 +00:00
hio_htrd_setrecbs ( file - > client - > htrd , & file - > client_htrd_org_recbs ) ;
2020-07-08 05:24:40 +00:00
}
2020-11-16 16:52:49 +00:00
if ( ! file - > client_disconnected )
2020-07-08 05:24:40 +00:00
{
2021-07-22 07:30:20 +00:00
if ( ! file - > keep_alive | | hio_dev_sck_read ( file - > client - > sck , 1 ) < = - 1 )
2020-07-08 05:24:40 +00:00
{
2022-10-08 07:47:55 +00:00
HIO_DEBUG3 ( hio , " HTTS(%p) - file(c=%d,p=%d) halting client for failure to enable input watching \n " , file - > htts , ( int ) file - > client - > sck - > hnd , file - > peer ) ;
2021-07-22 07:30:20 +00:00
hio_dev_sck_halt ( file - > client - > sck ) ;
2020-07-08 05:24:40 +00:00
}
}
}
2021-07-22 07:30:20 +00:00
static void file_client_on_disconnect ( hio_dev_sck_t * sck )
2020-07-08 05:24:40 +00:00
{
2022-10-08 07:47:55 +00:00
hio_t * hio = sck - > hio ;
2021-07-22 07:30:20 +00:00
hio_svc_htts_cli_t * cli = hio_dev_sck_getxtn ( sck ) ;
2020-11-16 16:52:49 +00:00
file_t * file = ( file_t * ) cli - > rsrc ;
2022-10-08 07:47:55 +00:00
HIO_ASSERT ( hio , sck = = cli - > sck ) ;
HIO_ASSERT ( hio , sck = = file - > client - > sck ) ;
HIO_DEBUG3 ( cli - > htts - > hio , " HTTS(%p) - file(c=%d,p=%d) client on_disconnect \n " , file - > client - > htts , ( int ) sck - > hnd , file - > peer ) ;
2020-11-16 16:52:49 +00:00
file - > client_disconnected = 1 ;
file - > client_org_on_disconnect ( sck ) ;
2022-10-08 07:47:55 +00:00
/* the original disconnect handler (listener_on_disconnect in http-svr.c)
* frees the file resource attached to the client . so it must not be accessed */
HIO_ASSERT ( hio , cli - > rsrc = = HIO_NULL ) ;
2020-07-08 05:24:40 +00:00
}
2021-07-22 07:30:20 +00:00
static int file_client_on_read ( hio_dev_sck_t * sck , const void * buf , hio_iolen_t len , const hio_skad_t * srcaddr )
2020-07-08 05:24:40 +00:00
{
2021-07-22 07:30:20 +00:00
hio_t * hio = sck - > hio ;
hio_svc_htts_cli_t * cli = hio_dev_sck_getxtn ( sck ) ;
2020-11-16 16:52:49 +00:00
file_t * file = ( file_t * ) cli - > rsrc ;
2020-07-08 05:24:40 +00:00
2021-07-22 07:30:20 +00:00
HIO_ASSERT ( hio , sck = = cli - > sck ) ;
2022-10-08 07:47:55 +00:00
HIO_ASSERT ( hio , sck = = file - > client - > sck ) ;
2020-07-08 05:24:40 +00:00
if ( len < = - 1 )
{
/* read error */
2022-10-08 07:47:55 +00:00
HIO_DEBUG3 ( cli - > htts - > hio , " HTTS(%p) - file(c=%d,p=%d) read error on client \n " , file - > client - > htts , ( int ) sck - > hnd , file - > peer ) ;
2020-07-08 05:24:40 +00:00
goto oops ;
}
2022-10-09 16:41:07 +00:00
if ( file - > peer < = - 1 )
2020-07-08 05:24:40 +00:00
{
2022-10-09 16:41:07 +00:00
/* the peer is gone or not even opened */
HIO_DEBUG3 ( cli - > htts - > hio , " HTTS(%p) - file(c=%d,p=%d) read on client, no peer to write \n " , file - > client - > htts , ( int ) sck - > hnd , file - > peer ) ;
2020-07-08 05:24:40 +00:00
goto oops ; /* do what? just return 0? */
}
if ( len = = 0 )
{
/* EOF on the client side. arrange to close */
2022-10-08 07:47:55 +00:00
HIO_DEBUG3 ( cli - > htts - > hio , " HTTS(%p) - file(c=%d,p=%d) EOF detected on client \n " , file - > client - > htts , ( int ) sck - > hnd , file - > peer ) ;
2022-10-09 16:41:07 +00:00
file - > client_eof_detected = 1 ;
2020-07-08 05:24:40 +00:00
2020-11-16 16:52:49 +00:00
if ( ! ( file - > over & FILE_OVER_READ_FROM_CLIENT ) ) /* if this is true, EOF is received without file_client_htrd_poke() */
2020-07-08 05:24:40 +00:00
{
2022-10-09 16:41:07 +00:00
int n ;
n = file_write_to_peer ( file , HIO_NULL , 0 ) ;
2020-11-16 16:52:49 +00:00
file_mark_over ( file , FILE_OVER_READ_FROM_CLIENT ) ;
2022-10-09 16:41:07 +00:00
if ( n < = - 1 ) goto oops ;
2020-07-08 05:24:40 +00:00
}
}
else
{
2021-07-22 07:30:20 +00:00
hio_oow_t rem ;
2020-07-08 05:24:40 +00:00
2021-07-22 07:30:20 +00:00
HIO_ASSERT ( hio , ! ( file - > over & FILE_OVER_READ_FROM_CLIENT ) ) ;
2020-07-08 05:24:40 +00:00
2021-07-22 07:30:20 +00:00
if ( hio_htrd_feed ( cli - > htrd , buf , len , & rem ) < = - 1 ) goto oops ;
2020-07-08 05:24:40 +00:00
if ( rem > 0 )
{
/* TODO store this to client buffer. once the current resource is completed, arrange to call on_read() with it */
2022-10-08 07:47:55 +00:00
HIO_DEBUG3 ( cli - > htts - > hio , " HTTS(%p) - file(c=%d,p=%d) excessive data after contents on client \n " , file - > client - > htts , ( int ) sck - > hnd , file - > peer ) ;
2020-07-08 05:24:40 +00:00
}
}
return 0 ;
oops :
2020-11-16 16:52:49 +00:00
file_halt_participating_devices ( file ) ;
2020-07-08 05:24:40 +00:00
return 0 ;
}
2021-07-22 07:30:20 +00:00
static int file_client_on_write ( hio_dev_sck_t * sck , hio_iolen_t wrlen , void * wrctx , const hio_skad_t * dstaddr )
2020-07-08 05:24:40 +00:00
{
2021-07-22 07:30:20 +00:00
hio_t * hio = sck - > hio ;
hio_svc_htts_cli_t * cli = hio_dev_sck_getxtn ( sck ) ;
2020-11-16 16:52:49 +00:00
file_t * file = ( file_t * ) cli - > rsrc ;
2020-07-08 05:24:40 +00:00
2022-10-08 07:47:55 +00:00
HIO_ASSERT ( hio , sck = = cli - > sck ) ;
HIO_ASSERT ( hio , sck = = file - > client - > sck ) ;
2020-07-08 05:24:40 +00:00
if ( wrlen < = - 1 )
{
2022-10-08 07:47:55 +00:00
HIO_DEBUG3 ( hio , " HTTS(%p) - file(c=%d,p=%d) unable to write to client \n " , file - > client - > htts , ( int ) sck - > hnd , file - > peer ) ;
2020-07-08 05:24:40 +00:00
goto oops ;
}
if ( wrlen = = 0 )
{
/* if the connect is keep-alive, this part may not be called */
2020-11-16 16:52:49 +00:00
file - > num_pending_writes_to_client - - ;
2021-07-22 07:30:20 +00:00
HIO_ASSERT ( hio , file - > num_pending_writes_to_client = = 0 ) ;
2022-10-08 07:47:55 +00:00
HIO_DEBUG3 ( hio , " HTTS(%p) - file(c=%d,p=%d) indicated EOF to client \n " , file - > client - > htts , ( int ) sck - > hnd , file - > peer ) ;
2020-07-08 05:24:40 +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-07-09 09:54:13 +00:00
2020-11-16 16:52:49 +00:00
file_mark_over ( file , FILE_OVER_WRITE_TO_CLIENT ) ;
2020-07-08 05:24:40 +00:00
}
else
{
2021-07-22 07:30:20 +00:00
HIO_ASSERT ( hio , file - > num_pending_writes_to_client > 0 ) ;
2020-11-16 16:52:49 +00:00
file - > num_pending_writes_to_client - - ;
2020-07-08 05:24:40 +00:00
2021-07-22 07:30:20 +00:00
if ( file - > req_method = = HIO_HTTP_GET )
2020-11-16 16:52:49 +00:00
file_send_contents_to_client ( file ) ;
2020-07-08 05:24:40 +00:00
2020-11-16 16:52:49 +00:00
if ( ( file - > over & FILE_OVER_READ_FROM_PEER ) & & file - > num_pending_writes_to_client < = 0 )
2020-07-08 05:24:40 +00:00
{
2020-11-16 16:52:49 +00:00
file_mark_over ( file , FILE_OVER_WRITE_TO_CLIENT ) ;
2020-07-08 05:24:40 +00:00
}
}
return 0 ;
oops :
2020-11-16 16:52:49 +00:00
file_halt_participating_devices ( file ) ;
2020-07-08 05:24:40 +00:00
return 0 ;
}
2020-07-15 10:30:12 +00:00
/* --------------------------------------------------------------------- */
2020-07-08 05:24:40 +00:00
2021-07-22 07:30:20 +00:00
static int file_client_htrd_poke ( hio_htrd_t * htrd , hio_htre_t * req )
2020-07-15 10:30:12 +00:00
{
/* client request got completed */
2021-07-22 07:30:20 +00:00
hio_svc_htts_cli_htrd_xtn_t * htrdxtn = ( hio_svc_htts_cli_htrd_xtn_t * ) hio_htrd_getxtn ( htrd ) ;
hio_dev_sck_t * sck = htrdxtn - > sck ;
hio_svc_htts_cli_t * cli = hio_dev_sck_getxtn ( sck ) ;
2020-11-16 16:52:49 +00:00
file_t * file = ( file_t * ) cli - > rsrc ;
2020-07-08 05:24:40 +00:00
2020-07-15 10:30:12 +00:00
/* indicate EOF to the client peer */
2021-07-22 07:30:20 +00:00
if ( file_write_to_peer ( file , HIO_NULL , 0 ) < = - 1 ) return - 1 ;
2020-07-08 05:24:40 +00:00
2021-07-22 07:30:20 +00:00
if ( file - > req_method ! = HIO_HTTP_GET )
2020-07-15 10:30:12 +00:00
{
2022-12-26 17:03:53 +00:00
if ( file_send_final_status_to_client ( file , HIO_HTTP_STATUS_OK , 0 , 0 ) < = - 1 ) return - 1 ;
2020-07-08 05:24:40 +00:00
}
2020-11-16 16:52:49 +00:00
file_mark_over ( file , FILE_OVER_READ_FROM_CLIENT ) ;
2020-07-08 05:24:40 +00:00
return 0 ;
}
2020-07-15 10:30:12 +00:00
2021-07-22 07:30:20 +00:00
static int file_client_htrd_push_content ( hio_htrd_t * htrd , hio_htre_t * req , const hio_bch_t * data , hio_oow_t dlen )
2020-07-15 10:30:12 +00:00
{
2021-07-22 07:30:20 +00:00
hio_svc_htts_cli_htrd_xtn_t * htrdxtn = ( hio_svc_htts_cli_htrd_xtn_t * ) hio_htrd_getxtn ( htrd ) ;
hio_dev_sck_t * sck = htrdxtn - > sck ;
hio_svc_htts_cli_t * cli = hio_dev_sck_getxtn ( sck ) ;
2020-11-16 16:52:49 +00:00
file_t * file = ( file_t * ) cli - > rsrc ;
2020-07-15 10:30:12 +00:00
2021-07-22 07:30:20 +00:00
HIO_ASSERT ( sck - > hio , cli - > sck = = sck ) ;
2020-11-16 16:52:49 +00:00
return file_write_to_peer ( file , data , dlen ) ;
2020-07-15 10:30:12 +00:00
}
2021-07-22 07:30:20 +00:00
static hio_htrd_recbs_t file_client_htrd_recbs =
2020-07-15 10:30:12 +00:00
{
2021-07-22 07:30:20 +00:00
HIO_NULL ,
2020-07-15 10:30:12 +00:00
file_client_htrd_poke ,
file_client_htrd_push_content
} ;
/* --------------------------------------------------------------------- */
2020-07-08 05:24:40 +00:00
2021-07-22 07:30:20 +00:00
static int file_send_header_to_client ( file_t * file , int status_code , int force_close , const hio_bch_t * mime_type )
2020-07-09 09:54:13 +00:00
{
2021-07-22 07:30:20 +00:00
hio_svc_htts_cli_t * cli = file - > client ;
hio_bch_t dtbuf [ 64 ] ;
hio_foff_t content_length ;
2020-07-09 09:54:13 +00:00
2021-07-22 07:30:20 +00:00
hio_svc_htts_fmtgmtime ( cli - > htts , HIO_NULL , dtbuf , HIO_COUNTOF ( dtbuf ) ) ;
2020-07-09 09:54:13 +00:00
2020-11-16 16:52:49 +00:00
if ( ! force_close ) force_close = ! file - > keep_alive ;
2020-07-09 09:54:13 +00:00
2020-11-16 16:52:49 +00:00
content_length = file - > end_offset - file - > start_offset + 1 ;
2022-10-09 16:41:07 +00:00
if ( status_code = = HIO_HTTP_STATUS_OK & & file - > total_size ! = content_length ) status_code = HIO_HTTP_STATUS_PARTIAL_CONTENT ;
2020-07-13 10:01:42 +00:00
2022-12-16 16:24:10 +00:00
if ( hio_becs_fmt ( cli - > sbuf , " HTTP/%d.%d %d %hs \r \n Server: %hs \r \n Date: %s \r \n Connection: %hs \r \n Accept-Ranges: bytes \r \n " ,
2020-11-16 16:52:49 +00:00
file - > req_version . major , file - > req_version . minor ,
2021-07-22 07:30:20 +00:00
status_code , hio_http_status_to_bcstr ( status_code ) ,
2020-07-09 09:54:13 +00:00
cli - > htts - > server_name , dtbuf ,
2022-12-16 16:24:10 +00:00
( force_close ? " close " : " keep-alive " ) ) = = ( hio_oow_t ) - 1 ) return - 1 ;
2020-07-20 14:08:25 +00:00
2022-12-16 16:24:10 +00:00
/* Content-Type is not set if mime_type is null or blank */
if ( mime_type & & mime_type [ 0 ] ! = ' \0 ' & &
hio_becs_fcat ( cli - > sbuf , " Content-Type: %hs \r \n " , mime_type ) = = ( hio_oow_t ) - 1 ) return - 1 ;
2022-12-26 17:03:53 +00:00
if ( ( file - > req_method = = HIO_HTTP_GET | | file - > req_method = = HIO_HTTP_HEAD ) & &
hio_becs_fcat ( cli - > sbuf , " ETag: %hs \r \n " , file - > peer_etag ) = = ( hio_oow_t ) - 1 ) return - 1 ;
if ( status_code = = HIO_HTTP_STATUS_PARTIAL_CONTENT & &
hio_becs_fcat ( cli - > sbuf , " Content-Ranges: bytes %ju-%ju/%ju \r \n " , ( hio_uintmax_t ) file - > start_offset , ( hio_uintmax_t ) file - > end_offset , ( hio_uintmax_t ) file - > total_size ) = = ( hio_oow_t ) - 1 ) return - 1 ;
2020-08-10 17:03:54 +00:00
/* ----- */
// TODO: Allow-Contents
// Allow-Headers... support custom headers...
2021-07-22 07:30:20 +00:00
if ( hio_becs_fcat ( cli - > sbuf , " Access-Control-Allow-Origin: * \r \n " , ( hio_uintmax_t ) content_length ) = = ( hio_oow_t ) - 1 ) return - 1 ;
2020-08-10 17:03:54 +00:00
/* ----- */
2021-07-22 07:30:20 +00:00
if ( hio_becs_fcat ( cli - > sbuf , " Content-Length: %ju \r \n \r \n " , ( hio_uintmax_t ) content_length ) = = ( hio_oow_t ) - 1 ) return - 1 ;
2020-07-09 09:54:13 +00:00
2021-07-22 07:30:20 +00:00
return file_write_to_client ( file , HIO_BECS_PTR ( cli - > sbuf ) , HIO_BECS_LEN ( cli - > sbuf ) ) ;
2020-07-09 09:54:13 +00:00
}
2021-07-22 07:30:20 +00:00
static void send_contents_to_client_later ( hio_t * hio , const hio_ntime_t * now , hio_tmrjob_t * tmrjob )
2020-07-15 10:30:12 +00:00
{
2020-11-16 16:52:49 +00:00
file_t * file = ( file_t * ) tmrjob - > ctx ;
if ( file_send_contents_to_client ( file ) < = - 1 )
2020-07-15 10:30:12 +00:00
{
2020-11-16 16:52:49 +00:00
file_halt_participating_devices ( file ) ;
2020-07-15 10:30:12 +00:00
}
}
2020-11-16 16:52:49 +00:00
static int file_send_contents_to_client ( file_t * file )
2020-07-09 09:54:13 +00:00
{
2021-07-22 07:30:20 +00:00
hio_t * hio = file - > htts - > hio ;
hio_foff_t lim ;
2020-07-09 09:54:13 +00:00
2020-11-16 16:52:49 +00:00
if ( file - > cur_offset > file - > end_offset )
2020-07-09 09:54:13 +00:00
{
2020-07-13 10:01:42 +00:00
/* reached the end */
2020-11-16 16:52:49 +00:00
file_mark_over ( file , FILE_OVER_READ_FROM_PEER ) ;
2020-07-13 10:01:42 +00:00
return 0 ;
}
2020-11-16 16:52:49 +00:00
lim = file - > end_offset - file - > cur_offset + 1 ;
if ( file - > sendfile_ok )
2020-07-13 10:01:42 +00:00
{
2020-07-19 16:19:18 +00:00
if ( lim > 0x7FFF0000 ) lim = 0x7FFF0000 ; /* TODO: change this... */
2020-11-16 16:52:49 +00:00
if ( file_sendfile_to_client ( file , file - > cur_offset , lim ) < = - 1 ) return - 1 ;
file - > cur_offset + = lim ;
2020-07-09 09:54:13 +00:00
}
2020-07-19 06:23:43 +00:00
else
{
ssize_t n ;
2020-07-09 09:54:13 +00:00
2021-07-22 07:30:20 +00:00
n = read ( file - > peer , file - > peer_buf , ( lim < HIO_SIZEOF ( file - > peer_buf ) ? lim : HIO_SIZEOF ( file - > peer_buf ) ) ) ;
2020-07-19 06:23:43 +00:00
if ( n = = - 1 )
{
2021-07-22 07:30:20 +00:00
if ( ( errno = = EAGAIN | | errno = = EINTR ) & & file - > peer_tmridx = = HIO_TMRIDX_INVALID )
2020-07-19 06:23:43 +00:00
{
2021-07-22 07:30:20 +00:00
hio_tmrjob_t tmrjob ;
2020-07-19 06:23:43 +00:00
/* use a timer job for a new sending attempt */
2021-07-22 07:30:20 +00:00
HIO_MEMSET ( & tmrjob , 0 , HIO_SIZEOF ( tmrjob ) ) ;
2020-11-16 16:52:49 +00:00
tmrjob . ctx = file ;
2020-07-19 06:23:43 +00:00
/*tmrjob.when = leave it at 0 for immediate firing.*/
tmrjob . handler = send_contents_to_client_later ;
2020-11-16 16:52:49 +00:00
tmrjob . idxptr = & file - > peer_tmridx ;
2021-07-22 07:30:20 +00:00
return hio_instmrjob ( hio , & tmrjob ) = = HIO_TMRIDX_INVALID ? - 1 : 0 ;
2020-07-19 06:23:43 +00:00
}
2020-07-15 10:30:12 +00:00
2020-07-19 06:23:43 +00:00
return - 1 ;
}
else if ( n = = 0 )
{
2022-12-21 14:57:51 +00:00
/* no more data to read - this must not happen unless file size changed while the file is open. */
2020-07-19 06:23:43 +00:00
/* TODO: I probably must close the connection by force??? */
2020-11-16 16:52:49 +00:00
file_mark_over ( file , FILE_OVER_READ_FROM_PEER ) ;
2020-07-19 06:23:43 +00:00
return - 1 ;
}
2020-11-16 16:52:49 +00:00
if ( file_write_to_client ( file , file - > peer_buf , n ) < = - 1 ) return - 1 ;
2020-07-15 10:30:12 +00:00
2020-11-16 16:52:49 +00:00
file - > cur_offset + = n ;
2020-07-19 06:23:43 +00:00
2020-11-16 16:52:49 +00:00
/* if (file->cur_offset > file->end_offset) should i check this or wait until this function is invoked?
file_mark_over ( file , FILE_OVER_READ_FROM_PEER ) ; */
2020-07-19 06:23:43 +00:00
}
2020-07-15 10:30:12 +00:00
return 0 ;
}
2022-10-09 16:41:07 +00:00
# define ERRNO_TO_STATUS_CODE(x) ( \
2022-12-26 17:03:53 +00:00
( ( x ) = = ENOENT | | ( x ) = = ENOTDIR ) ? HIO_HTTP_STATUS_NOT_FOUND : \
2022-10-09 16:41:07 +00:00
( ( x ) = = EPERM | | ( x ) = = EACCES ) ? HIO_HTTP_STATUS_FORBIDDEN : HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR \
)
static HIO_INLINE int process_range_header ( file_t * file , hio_htre_t * req , int * error_status )
2020-07-15 10:30:12 +00:00
{
struct stat st ;
2021-07-22 07:30:20 +00:00
const hio_htre_hdrval_t * tmp ;
hio_oow_t etag_len ;
2020-07-15 10:30:12 +00:00
2020-11-16 16:52:49 +00:00
if ( fstat ( file - > peer , & st ) < = - 1 )
2020-07-13 10:01:42 +00:00
{
2022-10-09 16:41:07 +00:00
* error_status = ERRNO_TO_STATUS_CODE ( errno ) ;
2020-07-13 10:01:42 +00:00
return - 1 ;
}
2020-07-20 14:08:25 +00:00
2020-08-10 17:03:54 +00:00
if ( ( st . st_mode & S_IFMT ) ! = S_IFREG )
2020-08-02 16:07:42 +00:00
{
/* TODO: support directory listing if S_IFDIR? still disallow special files. */
2022-10-09 16:41:07 +00:00
* error_status = HIO_HTTP_STATUS_FORBIDDEN ;
2020-08-02 16:07:42 +00:00
return - 1 ;
}
2022-12-26 17:03:53 +00:00
# if defined(HAVE_STRUCT_STAT_MTIM)
etag_len = hio_fmt_uintmax_to_bcstr ( & file - > peer_etag [ 0 ] , HIO_COUNTOF ( file - > peer_etag ) , st . st_mtim . tv_sec , 16 , - 1 , ' \0 ' , HIO_NULL ) ;
file - > peer_etag [ etag_len + + ] = ' - ' ;
# if defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
etag_len + = hio_fmt_uintmax_to_bcstr ( & file - > peer_etag [ etag_len ] , HIO_COUNTOF ( file - > peer_etag ) , st . st_mtim . tv_nsec , 16 , - 1 , ' \0 ' , HIO_NULL ) ;
file - > peer_etag [ etag_len + + ] = ' - ' ;
2022-06-17 05:35:30 +00:00
# endif
2022-12-26 17:03:53 +00:00
# else
etag_len = hio_fmt_uintmax_to_bcstr ( & file - > peer_etag [ 0 ] , HIO_COUNTOF ( file - > peer_etag ) , st . st_mtime , 16 , - 1 , ' \0 ' , HIO_NULL ) ;
file - > peer_etag [ etag_len + + ] = ' - ' ;
# endif
etag_len + = hio_fmt_uintmax_to_bcstr ( & file - > peer_etag [ etag_len ] , HIO_COUNTOF ( file - > peer_etag ) - etag_len , st . st_size , 16 , - 1 , ' \0 ' , HIO_NULL ) ;
file - > peer_etag [ etag_len + + ] = ' - ' ;
etag_len + = hio_fmt_uintmax_to_bcstr ( & file - > peer_etag [ etag_len ] , HIO_COUNTOF ( file - > peer_etag ) - etag_len , st . st_ino , 16 , - 1 , ' \0 ' , HIO_NULL ) ;
file - > peer_etag [ etag_len + + ] = ' - ' ;
hio_fmt_uintmax_to_bcstr ( & file - > peer_etag [ etag_len ] , HIO_COUNTOF ( file - > peer_etag ) - etag_len , st . st_dev , 16 , - 1 , ' \0 ' , HIO_NULL ) ;
tmp = hio_htre_getheaderval ( req , " If-None-Match " ) ;
if ( tmp & & hio_comp_bcstr ( file - > peer_etag , tmp - > ptr , 0 ) = = 0 ) file - > etag_match = 1 ;
2020-07-20 14:08:25 +00:00
2020-11-16 16:52:49 +00:00
file - > end_offset = st . st_size ;
2020-07-13 10:01:42 +00:00
2021-07-22 07:30:20 +00:00
tmp = hio_htre_getheaderval ( req , " Range " ) ; /* TODO: support multiple ranges? */
2020-07-15 10:30:12 +00:00
if ( tmp )
{
2021-07-22 07:30:20 +00:00
hio_http_range_t range ;
2020-07-15 10:30:12 +00:00
2021-07-22 07:30:20 +00:00
if ( hio_parse_http_range_bcstr ( tmp - > ptr , & range ) < = - 1 )
2020-07-15 10:30:12 +00:00
{
range_not_satisifiable :
2022-10-09 16:41:07 +00:00
* error_status = HIO_HTTP_STATUS_RANGE_NOT_SATISFIABLE ;
2020-07-15 10:30:12 +00:00
return - 1 ;
}
switch ( range . type )
{
2021-07-22 07:30:20 +00:00
case HIO_HTTP_RANGE_PROPER :
2020-07-15 10:30:12 +00:00
/* Range XXXX-YYYY */
if ( range . to > = st . st_size ) goto range_not_satisifiable ;
2020-11-16 16:52:49 +00:00
file - > start_offset = range . from ;
file - > end_offset = range . to ;
2020-07-15 10:30:12 +00:00
break ;
2021-07-22 07:30:20 +00:00
case HIO_HTTP_RANGE_PREFIX :
2020-07-15 10:30:12 +00:00
/* Range: XXXX- */
if ( range . from > = st . st_size ) goto range_not_satisifiable ;
2020-11-16 16:52:49 +00:00
file - > start_offset = range . from ;
file - > end_offset = st . st_size - 1 ;
2020-07-15 10:30:12 +00:00
break ;
2021-07-22 07:30:20 +00:00
case HIO_HTTP_RANGE_SUFFIX :
2020-07-15 10:30:12 +00:00
/* Range: -XXXX */
if ( range . to > = st . st_size ) goto range_not_satisifiable ;
2020-11-16 16:52:49 +00:00
file - > start_offset = st . st_size - range . to ;
file - > end_offset = st . st_size - 1 ;
2020-07-15 10:30:12 +00:00
break ;
}
2020-07-09 09:54:13 +00:00
2020-11-16 16:52:49 +00:00
if ( file - > start_offset > 0 )
2022-10-09 16:41:07 +00:00
{
if ( lseek ( file - > peer , file - > start_offset , SEEK_SET ) < = - 1 )
{
* error_status = ERRNO_TO_STATUS_CODE ( errno ) ;
return - 1 ;
}
}
2020-07-15 10:30:12 +00:00
}
else
{
2020-11-16 16:52:49 +00:00
file - > start_offset = 0 ;
file - > end_offset = st . st_size - 1 ;
2020-07-15 10:30:12 +00:00
}
2020-11-16 16:52:49 +00:00
file - > cur_offset = file - > start_offset ;
file - > total_size = st . st_size ;
2020-07-09 09:54:13 +00:00
return 0 ;
}
2022-12-31 15:20:07 +00:00
static int open_peer_with_mode ( file_t * file , const hio_bch_t * actual_file , int flags , int * error_status , const hio_bch_t * * res_mime_type )
2020-07-15 10:30:12 +00:00
{
2022-12-15 15:24:31 +00:00
struct stat st ;
2022-10-09 16:41:07 +00:00
flags | = O_NONBLOCK ;
# if defined(O_CLOEXEC)
flags | = O_CLOEXEC ;
2020-07-17 10:14:15 +00:00
# endif
2022-10-09 16:41:07 +00:00
# if defined(O_LARGEFILE)
flags | = O_LARGEFILE ;
2020-07-15 10:30:12 +00:00
# endif
2022-10-09 16:41:07 +00:00
file - > peer = open ( actual_file , flags , 0644 ) ;
if ( HIO_UNLIKELY ( file - > peer < = - 1 ) )
{
* error_status = ERRNO_TO_STATUS_CODE ( errno ) ;
return - 1 ;
2020-07-15 10:30:12 +00:00
}
2022-12-31 15:20:07 +00:00
if ( ( ( flags | O_RDONLY ) & & fstat ( file - > peer , & st ) > = 0 & & S_ISDIR ( st . st_mode ) ) & & /* only for read operation */
( file - > cbs & & file - > cbs - > open_dir_list ) ) /* directory listing is enabled */
2022-12-15 15:24:31 +00:00
{
int alt_fd ;
2022-12-26 17:03:53 +00:00
if ( ! file - > req_qpath_ending_with_slash )
{
* error_status = HIO_HTTP_STATUS_MOVED_PERMANENTLY ;
close ( file - > peer ) ;
file - > peer = - 1 ;
return - 1 ;
}
2022-12-31 15:20:07 +00:00
alt_fd = file - > cbs - > open_dir_list ( file - > htts , file - > req_qpath , actual_file , res_mime_type , file - > cbs - > ctx ) ;
if ( alt_fd > = 0 )
2022-12-15 15:24:31 +00:00
{
2022-12-31 15:20:07 +00:00
close ( file - > peer ) ;
file - > peer = alt_fd ;
2022-12-15 15:24:31 +00:00
}
}
2022-12-31 15:20:07 +00:00
else
2022-12-16 16:24:10 +00:00
{
2022-12-31 15:20:07 +00:00
if ( res_mime_type & & file - > cbs & & file - > cbs - > get_mime_type )
2022-12-16 16:24:10 +00:00
{
2022-12-31 15:20:07 +00:00
const hio_bch_t * mime_type ;
mime_type = file - > cbs - > get_mime_type ( file - > htts , file - > req_qpath , actual_file , file - > cbs - > ctx ) ;
if ( mime_type ) * res_mime_type = mime_type ;
2022-12-16 16:24:10 +00:00
}
}
2022-10-09 16:41:07 +00:00
return 0 ;
2020-07-15 10:30:12 +00:00
}
2022-10-09 16:41:07 +00:00
static HIO_INLINE void set_tcp_cork ( hio_dev_sck_t * sck )
2020-07-16 04:18:01 +00:00
{
2022-10-09 16:41:07 +00:00
# if defined(TCP_CORK)
int tcp_cork = 1 ;
# if defined(SOL_TCP)
hio_dev_sck_setsockopt ( sck , SOL_TCP , TCP_CORK , & tcp_cork , HIO_SIZEOF ( tcp_cork ) ) ;
# elif defined(IPPROTO_TCP)
hio_dev_sck_setsockopt ( sck , IPPROTO_TCP , TCP_CORK , & tcp_cork , HIO_SIZEOF ( tcp_cork ) ) ;
# endif
2020-07-16 04:18:01 +00:00
# endif
}
2022-12-21 14:57:52 +00:00
int hio_svc_htts_dofile ( hio_svc_htts_t * htts , hio_dev_sck_t * csck , hio_htre_t * req , const hio_bch_t * docroot , const hio_bch_t * filepath , const hio_bch_t * mime_type , int options , hio_svc_htts_file_cbs_t * cbs )
2020-07-08 05:24:40 +00:00
{
2021-07-22 07:30:20 +00:00
hio_t * hio = htts - > hio ;
hio_svc_htts_cli_t * cli = hio_dev_sck_getxtn ( csck ) ;
file_t * file = HIO_NULL ;
hio_bch_t * actual_file = HIO_NULL ;
2022-12-26 17:03:53 +00:00
hio_bch_t * req_qpath = HIO_NULL ;
int dir_redirect = 0 ;
2022-10-09 16:41:07 +00:00
int status_code ;
2020-07-08 05:24:40 +00:00
/* ensure that you call this function before any contents is received */
2021-07-22 07:30:20 +00:00
HIO_ASSERT ( hio , hio_htre_getcontentlen ( req ) = = 0 ) ;
2022-10-08 07:47:55 +00:00
HIO_ASSERT ( hio , cli - > sck = = csck ) ;
HIO_DEBUG5 ( hio , " HTTS(%p) - file(c=%d) - [%hs] %hs%hs \n " , htts , ( int ) csck - > hnd , cli - > cli_addr_bcstr , ( docroot [ 0 ] = = ' / ' & & docroot [ 1 ] = = ' \0 ' & & filepath [ 0 ] = = ' / ' ? " " : docroot ) , filepath ) ;
2020-07-08 05:24:40 +00:00
2022-12-26 17:03:53 +00:00
/* TODO: may have to use getorgqpath() for the percent-decoding... */
req_qpath = hio_dupbcstr ( hio , hio_htre_getqpath ( req ) , HIO_NULL ) ;
if ( HIO_UNLIKELY ( ! req_qpath ) ) goto oops ;
2021-07-22 07:30:20 +00:00
actual_file = hio_svc_htts_dupmergepaths ( htts , docroot , filepath ) ;
if ( HIO_UNLIKELY ( ! actual_file ) ) goto oops ;
2020-07-08 05:24:40 +00:00
2021-07-22 07:30:20 +00:00
file = ( file_t * ) hio_svc_htts_rsrc_make ( htts , HIO_SIZEOF ( * file ) , file_on_kill ) ;
if ( HIO_UNLIKELY ( ! file ) ) goto oops ;
2020-07-08 05:24:40 +00:00
2022-10-09 16:41:07 +00:00
file - > options = options ;
2022-12-21 14:57:52 +00:00
file - > cbs = cbs ; /* the given pointer must outlive the lifespan of the while file handling cycle. */
2020-11-16 16:52:49 +00:00
file - > client = cli ;
2021-07-22 07:30:20 +00:00
file - > sendfile_ok = hio_dev_sck_sendfileok ( cli - > sck ) ;
2020-11-16 16:52:49 +00:00
/*file->num_pending_writes_to_client = 0;
file - > num_pending_writes_to_peer = 0 ; */
2021-07-22 07:30:20 +00:00
file - > req_version = * hio_htre_getversion ( req ) ;
file - > req_method = hio_htre_getqmethodtype ( req ) ;
file - > req_content_length_unlimited = hio_htre_getreqcontentlen ( req , & file - > req_content_length ) ;
2022-12-26 17:03:53 +00:00
file - > req_qpath = req_qpath ;
req_qpath = HIO_NULL ; /* delegated to the file resource */
2022-12-27 15:20:30 +00:00
file - > req_qpath_ending_with_slash = ( hio_htre_getqpathlen ( req ) > 0 & & hio_htre_getqpath ( req ) [ hio_htre_getqpathlen ( req ) - 1 ] = = ' / ' ) ;
2022-12-28 07:24:00 +00:00
file - > req_qpath_is_root = ( hio_htre_getqpathlen ( req ) = = 1 & & hio_htre_getqpath ( req ) [ 0 ] = = ' / ' ) ;
2020-07-08 05:24:40 +00:00
2020-11-16 16:52:49 +00:00
file - > client_org_on_read = csck - > on_read ;
file - > client_org_on_write = csck - > on_write ;
file - > client_org_on_disconnect = csck - > on_disconnect ;
2020-07-08 05:24:40 +00:00
csck - > on_read = file_client_on_read ;
csck - > on_write = file_client_on_write ;
csck - > on_disconnect = file_client_on_disconnect ;
2022-12-26 17:03:53 +00:00
file - > peer_tmridx = HIO_TMRIDX_INVALID ;
file - > peer = - 1 ;
2021-07-22 07:30:20 +00:00
HIO_ASSERT ( hio , cli - > rsrc = = HIO_NULL ) ; /* you must not call this function while cli->rsrc is not HIO_NULL */
2022-10-08 07:47:55 +00:00
HIO_SVC_HTTS_RSRC_ATTACH ( file , cli - > rsrc ) ; /* cli->rsrc = file with ref-count up */
2020-07-08 05:24:40 +00:00
# if !defined(FILE_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH)
2020-11-16 16:52:49 +00:00
if ( file - > req_content_length_unlimited )
2020-07-08 05:24:40 +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 ] */
2022-12-26 17:03:53 +00:00
if ( file_send_final_status_to_client ( file , HIO_HTTP_STATUS_LENGTH_REQUIRED , 1 , 0 ) < = - 1 ) goto oops ;
2020-07-08 05:24:40 +00:00
}
# endif
2021-07-22 07:30:20 +00:00
if ( req - > flags & HIO_HTRE_ATTR_EXPECT100 )
2020-07-08 05:24:40 +00:00
{
2022-10-09 16:41:07 +00:00
if ( ! ( options & HIO_SVC_HTTS_FILE_NO_100_CONTINUE ) & &
hio_comp_http_version_numbers ( & req - > version , 1 , 1 ) > = 0 & &
2020-11-16 16:52:49 +00:00
( file - > req_content_length_unlimited | | file - > req_content_length > 0 ) & &
2021-07-22 07:30:20 +00:00
( file - > req_method ! = HIO_HTTP_GET & & file - > req_method ! = HIO_HTTP_HEAD ) )
2020-07-08 05:24:40 +00:00
{
2021-07-22 07:30:20 +00:00
hio_bch_t msgbuf [ 64 ] ;
hio_oow_t msglen ;
2020-07-08 05:24:40 +00:00
2022-10-09 16:41:07 +00:00
msglen = hio_fmttobcstr ( hio , msgbuf , HIO_COUNTOF ( msgbuf ) , " HTTP/%d.%d %d %hs \r \n \r \n " , file - > req_version . major , file - > req_version . minor , HIO_HTTP_STATUS_CONTINUE , hio_http_status_to_bcstr ( HIO_HTTP_STATUS_CONTINUE ) ) ;
2020-11-16 16:52:49 +00:00
if ( file_write_to_client ( file , msgbuf , msglen ) < = - 1 ) goto oops ;
file - > ever_attempted_to_write_to_client = 0 ; /* reset this as it's polluted for 100 continue */
2020-07-08 05:24:40 +00:00
}
}
2021-07-22 07:30:20 +00:00
else if ( req - > flags & HIO_HTRE_ATTR_EXPECT )
2020-07-08 05:24:40 +00:00
{
/* 417 Expectation Failed */
2022-12-26 17:03:53 +00:00
file_send_final_status_to_client ( file , HIO_HTTP_STATUS_EXPECTATION_FAILED , 1 , HIO_NULL ) ;
2020-07-08 05:24:40 +00:00
goto oops ;
}
# if defined(FILE_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH)
2020-11-16 16:52:49 +00:00
if ( file - > req_content_length_unlimited )
2020-07-08 05:24:40 +00:00
{
/* change the callbacks to subscribe to contents to be uploaded */
2021-07-22 07:30:20 +00:00
file - > client_htrd_org_recbs = * hio_htrd_getrecbs ( file - > client - > htrd ) ;
2020-11-16 16:52:49 +00:00
file_client_htrd_recbs . peek = file - > client_htrd_org_recbs . peek ;
2021-07-22 07:30:20 +00:00
hio_htrd_setrecbs ( file - > client - > htrd , & file_client_htrd_recbs ) ;
2020-11-16 16:52:49 +00:00
file - > client_htrd_recbs_changed = 1 ;
2020-07-08 05:24:40 +00:00
}
else
{
# endif
2020-11-16 16:52:49 +00:00
if ( file - > req_content_length > 0 )
2020-07-08 05:24:40 +00:00
{
/* change the callbacks to subscribe to contents to be uploaded */
2021-07-22 07:30:20 +00:00
file - > client_htrd_org_recbs = * hio_htrd_getrecbs ( file - > client - > htrd ) ;
2020-11-16 16:52:49 +00:00
file_client_htrd_recbs . peek = file - > client_htrd_org_recbs . peek ;
2021-07-22 07:30:20 +00:00
hio_htrd_setrecbs ( file - > client - > htrd , & file_client_htrd_recbs ) ;
2020-11-16 16:52:49 +00:00
file - > client_htrd_recbs_changed = 1 ;
2020-07-08 05:24:40 +00:00
}
else
{
/* no content to be uploaded from the client */
2022-10-09 16:41:07 +00:00
#if 0
2020-07-08 05:24:40 +00:00
/* indicate EOF to the peer and disable input wathching from the client */
2021-07-22 07:30:20 +00:00
if ( file_write_to_peer ( file , HIO_NULL , 0 ) < = - 1 ) goto oops ;
2022-10-09 16:41:07 +00:00
HIO_ASSERT ( hio , file - > over | FILE_OVER_WRITE_TO_PEER ) ; /* must be set by the call to file_write_to_peer() above */
file_mark_over ( file , FILE_OVER_READ_FROM_CLIENT ) ;
# else
/* no peer is open yet. so simply set the mars forcibly instead of calling file_write_to_peer() with null data */
2020-11-16 16:52:49 +00:00
file_mark_over ( file , FILE_OVER_READ_FROM_CLIENT | FILE_OVER_WRITE_TO_PEER ) ;
2022-10-09 16:41:07 +00:00
# endif
2020-07-08 05:24:40 +00:00
}
# if defined(FILE_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH)
}
# endif
/* this may change later if Content-Length is included in the file output */
2021-07-22 07:30:20 +00:00
if ( req - > flags & HIO_HTRE_ATTR_KEEPALIVE )
2020-07-08 05:24:40 +00:00
{
2020-11-16 16:52:49 +00:00
file - > keep_alive = 1 ;
file - > res_mode_to_cli = FILE_RES_MODE_LENGTH ;
2020-07-08 05:24:40 +00:00
}
else
{
2020-11-16 16:52:49 +00:00
file - > keep_alive = 0 ;
file - > res_mode_to_cli = FILE_RES_MODE_CLOSE ;
2020-07-08 05:24:40 +00:00
}
2022-10-09 16:41:07 +00:00
switch ( file - > req_method )
2020-07-15 10:30:12 +00:00
{
2022-10-09 16:41:07 +00:00
case HIO_HTTP_GET :
2022-12-26 17:03:53 +00:00
case HIO_HTTP_HEAD :
2022-12-16 16:24:10 +00:00
{
const hio_bch_t * actual_mime_type = mime_type ;
2022-12-26 17:03:53 +00:00
if ( open_peer_with_mode ( file , actual_file , O_RDONLY , & status_code , ( mime_type ? HIO_NULL : & actual_mime_type ) ) < = - 1 )
2022-10-09 16:41:07 +00:00
{
2022-12-26 17:03:53 +00:00
dir_redirect = ( status_code = = HIO_HTTP_STATUS_MOVED_PERMANENTLY ) ; /* to handle the special rediret of a directory not ending with a slash in the request url */
2022-10-09 16:41:07 +00:00
goto done_with_status_code ;
}
2022-12-26 17:03:53 +00:00
if ( process_range_header ( file , req , & status_code ) < = - 1 ) goto done_with_status_code ;
2022-10-09 16:41:07 +00:00
2022-12-26 17:03:53 +00:00
if ( HIO_LIKELY ( file - > req_method = = HIO_HTTP_GET ) )
{
if ( file - > etag_match )
{
status_code = HIO_HTTP_STATUS_NOT_MODIFIED ;
goto done_with_status_code ;
}
2020-07-20 14:08:25 +00:00
2022-12-26 17:03:53 +00:00
/* normal full transfer */
# if defined(HAVE_POSIX_FADVISE)
posix_fadvise ( file - > peer , file - > start_offset , file - > end_offset - file - > start_offset + 1 , POSIX_FADV_SEQUENTIAL ) ;
# endif
set_tcp_cork ( file - > client - > sck ) ;
2022-10-09 16:41:07 +00:00
2022-12-26 17:03:53 +00:00
if ( file_send_header_to_client ( file , HIO_HTTP_STATUS_OK , 0 , actual_mime_type ) < = - 1 ) goto oops ;
if ( file_send_contents_to_client ( file ) < = - 1 ) goto oops ;
}
else
{
if ( file_send_header_to_client ( file , HIO_HTTP_STATUS_OK , 0 , actual_mime_type ) < = - 1 ) goto oops ;
/* no content must be transmitted for HEAD despite Content-Length in the header. */
goto done_with_status_code_2 ;
}
2022-10-09 16:41:07 +00:00
break ;
2022-12-16 16:24:10 +00:00
}
2022-10-09 16:41:07 +00:00
case HIO_HTTP_POST :
case HIO_HTTP_PUT :
if ( file - > options & HIO_SVC_HTTS_FILE_READ_ONLY )
{
status_code = HIO_HTTP_STATUS_METHOD_NOT_ALLOWED ;
goto done_with_status_code ;
}
2022-12-16 16:24:10 +00:00
if ( open_peer_with_mode ( file , actual_file , O_WRONLY | O_TRUNC | O_CREAT , & status_code , HIO_NULL ) < = - 1 ) goto done_with_status_code ;
2022-10-09 16:41:07 +00:00
/* the client input must be written to the peer side */
file_mark_over ( file , FILE_OVER_READ_FROM_PEER ) ;
break ;
case HIO_HTTP_DELETE :
if ( file - > options & HIO_SVC_HTTS_FILE_READ_ONLY )
{
status_code = HIO_HTTP_STATUS_METHOD_NOT_ALLOWED ;
goto done_with_status_code ;
}
if ( unlink ( actual_file ) < = - 1 )
{
if ( errno ! = EISDIR | | ( errno = = EISDIR & & rmdir ( actual_file ) < = - 1 ) )
{
status_code = ERRNO_TO_STATUS_CODE ( errno ) ;
goto done_with_status_code ;
}
}
status_code = HIO_HTTP_STATUS_OK ;
goto done_with_status_code ;
default :
status_code = HIO_HTTP_STATUS_METHOD_NOT_ALLOWED ;
done_with_status_code :
2022-12-26 17:03:53 +00:00
if ( file_send_final_status_to_client ( file , status_code , 0 , dir_redirect ) < = - 1 ) goto oops ;
done_with_status_code_2 :
2022-10-09 16:41:07 +00:00
file_mark_over ( file , FILE_OVER_READ_FROM_PEER | FILE_OVER_WRITE_TO_PEER ) ;
break ;
2020-07-17 10:14:15 +00:00
}
2020-07-09 09:54:13 +00:00
2020-11-16 16:52:49 +00:00
/* TODO: store current input watching state and use it when destroying the file data */
2021-07-22 07:30:20 +00:00
if ( hio_dev_sck_read ( csck , ! ( file - > over & FILE_OVER_READ_FROM_CLIENT ) ) < = - 1 ) goto oops ;
hio_freemem ( hio , actual_file ) ;
2020-07-08 05:24:40 +00:00
return 0 ;
oops :
2022-10-08 07:47:55 +00:00
HIO_DEBUG2 ( hio , " HTTS(%p) - file(c=%d) failure \n " , htts , csck - > hnd ) ;
2020-11-16 16:52:49 +00:00
if ( file ) file_halt_participating_devices ( file ) ;
2021-07-22 07:30:20 +00:00
if ( actual_file ) hio_freemem ( hio , actual_file ) ;
2022-12-26 17:03:53 +00:00
if ( req_qpath ) hio_freemem ( hio , req_qpath ) ;
2020-07-08 05:24:40 +00:00
return - 1 ;
}