📄 sendfile.cpp
字号:
sFileKey = (const char *)DeQuote( pValue );
}
else if ( !PIUtil_stricmp( KEY_CONF_NOTEKEY, pVariable ))
{
sNoteKey = (const char *)DeQuote( pValue );
}
else if ( !PIUtil_stricmp( KEY_CONF_NOTE, pVariable ))
{
if ( !LoadExpression( KEY_CONF_NOTE, &pAnnotation, 0, pValue, os ))
{ return 0; };
assert( pAnnotation );
}
else if ( !PIUtil_stricmp( KEY_CONF_DESCFILE, pVariable ))
{
sDescFile = (const char *)DeQuote( pValue );
PIFInfo *pFInfo = HTTPCore_getCachedFile( sDescFile );
if ( !pFInfo || strcspn( PIFInfo_getPath( pFInfo ), "/\\" ) < strlen( sDescFile ))
{
HTTPCore_releaseCachedFile( pFInfo );
os << "Error during read parameter '" << pVariable << "' or \
the filename '" << sDescFile << "' contains a path." << ends;
CONFIG_ERR( Object(), os.str() );
return 0;
};
HTTPCore_releaseCachedFile( pFInfo );
}
else if ( !PIUtil_stricmp( KEY_CONF_UPLOADPATH, pVariable ))
{
sPathInfo = (const char *)DeQuote( pValue );
}
else if ( !PIUtil_stricmp( KEY_CONF_UPLOADLIMIT, pVariable ))
{
iUploadLimit = atol( (const char *)pValue );
}
else if ( !PIUtil_stricmp( KEY_CONF_CHUNKLIMIT, pVariable ))
{
iChunkLimit = atol( (const char *)pValue );
}
else if ( !PIUtil_stricmp( KEY_CONF_METHODS, pVariable ))
{
/* ---
Split the options up by the bar ('|') character and then
strip trailing and leading whitespace from option before
attempting to match it.
--- */
StringTokenizer tTokens( pValue, "|" );
iMethods = 0;
for(int i = 0; i < tTokens.NumTokens(); i++)
{
const char *pToken = tTokens.GetToken( i );
/* --- skip leading whitespace --- */
for( ; *pToken && (isspace(*pToken)); pToken++ );
/* ---
include only to first whitespace trailing whitespace
--- */
int j=0;
for( ; pToken[j] && !(isspace(pToken[j])); j++ );
/* ---
j now contains the length of the first word in pToken
--- */
/* ---
cycle through the list of available flags
comparing them with this one
--- */
int i=0;
for( ; aMethodMap[i].pName; i++ )
{
if ( !PIUtil_strncmpi( aMethodMap[i].pName, pToken, j ) )
{ break; };
};
if ( !aMethodMap[i].pName )
{
/* --- flag not found --- */
PIString sTmp( pToken, j );
os << "Unknown HTTP method flag '" << sTmp << "'." << ends;
CONFIG_ERR( Object(), os.str() );
return 0;
};
iMethods |= aMethodMap[i].iFlag;
}; /* --- loop over tokens seperated by '|' --- */
}
else if ( !PIUtil_stricmp( KEY_CONF_RANGES, pVariable ))
{
iAllowRanges = PIUtil_stricmp((const char *)DeQuote( pValue ), KEY_HTTP_BYTES) == 0;
if ( iAllowRanges && (pHeaderPattern || pFooterPattern) )
{
os << "Ranges cannot be configured together with header/footer pattern." << ends;
CONFIG_ERR( Object(), os.str() );
return 0;
}
}
else
{
os << "Unknown directive '" << pVariable <<
"'" << ends;
CONFIG_ERR( Object(), os.str() );
return 0;
};
return 1;
};
/* ---
Create a translated path
--- */
int GetPathTranslated( PIHTTP &tPIHTTP, PIString &sPath )
{
/* --- make sub request context --- */
PIHTTP *pChildHTTP = PIHTTP_newChild( &tPIHTTP );
/* --- set path --- */
PIDB_replace( pChildHTTP->pResponseDB, PIDBTYPE_STRING,
KEY_INT_PATH, (void *)(const char *)sPath, 0 );
/* --- dispatch the sub request across the mapping phase --- */
int iRet = PIAPI_ERROR;
iRet = HTTPCore_dispatch( pChildHTTP, PH_MAPPING, PH_MAPPING );
/* --- copy the childs path to this path translated --- */
if ( iRet == PIAPI_COMPLETED && (( pChildHTTP->iStatus == 0 )
|| ( pChildHTTP->iStatus == ST_OK )))
{
sPath = (const char *)PIDB_lookup( pChildHTTP->pResponseDB, PIDBTYPE_STRING,
KEY_INT_PATH, 0 );
}
else
{
PIHTTP_delete( pChildHTTP );
return PIAPI_ERROR;
};
PIHTTP_delete( pChildHTTP );
return PIAPI_COMPLETED;
};
/* ---
Reset the mapped path before doing internal direct
--- */
int ResetPath( PIHTTP &tPIHTTP )
{
int iRet = PIAPI_COMPLETED;
const char *pURI = (const char *)PIDB_lookup( tPIHTTP.pRequestDB,
PIDBTYPE_STRING, KEY_HTTP_URI, 0 );
if ( pURI )
{
char *pTmp = new char[strlen(pURI)+1];
strcpy( pTmp, pURI );
/* ---
Then contract it to avoid side effects of '.' and '..' in
the URL
--- */
if ( HTTPUtil_contractPath( pTmp )>0 )
{
/* --- force a 403 (forbidden) error --- */
tPIHTTP.iStatus = ST_FORBIDDEN;
iRet = INT_REDIRECT;
}
else
{
PIDB_replace( tPIHTTP.pResponseDB, PIDBTYPE_STRING,
KEY_INT_PATH, pTmp, 0 );
};
delete [] pTmp;
}
else
{
HTTPCore_logError( &tPIHTTP, "Could not set request path\n" );
iRet = HTTPUtil_doHTTPError( &tPIHTTP, ST_INTERNALERROR );
};
return iRet;
}
/* ---
Load a description file, if one exists and contains descriptions return 1
else 0
--- */
int LoadDescriptionFile( PIDB *pDescriptionDB, PIString sPath ) const
{
assert( pDescriptionDB );
assert( sPath );
/* --- get the file --- */
PIFInfo *pFInfo = HTTPCore_getCachedFile( sPath );
if ( !pFInfo ) return 0;
if ( PIFInfo_exists( pFInfo ) && PIFInfo_isRegular( pFInfo ))
{
enum { BUF_SIZE=1023 };
char szBuf[BUF_SIZE+1];
*szBuf = '\0';
ifstream ifs( PIFInfo_getPath( pFInfo ) );
while( ifs && !ifs.eof() )
{
ifs.getline( szBuf, BUF_SIZE );
if ( strlen( szBuf ))
{
DescriptionMap *pDesc = PI_NEW( DescriptionMap( szBuf ));
PIDB_add( pDescriptionDB, PIDBTYPE_OPAQUE, pDesc->sFileName,
(void *)pDesc, 0 );
};
};
};
HTTPCore_releaseCachedFile( pFInfo );
return 1;
};
/* ---
Save a description file, success return 1 else 0
--- */
int SaveDescriptionFile( PIDB *pDescriptionDB, PIString sPath ) const
{
assert( pDescriptionDB );
assert( sPath );
/* --- get the file --- */
PIFInfo *pFInfo = HTTPCore_getCachedFile( sPath );
if ( !pFInfo ) return 0;
if ( PIFInfo_exists( pFInfo ) && !PIFInfo_isRegular( pFInfo )) return 0;
ofstream ofs( PIFInfo_getPath( pFInfo ) );
PIDBIterator *pIter = PIDB_getIterator( pDescriptionDB, PIDBTYPE_OPAQUE, 0, 0 );
if ( pIter )
{
for( ; pIter && PIDBIterator_atValidElement( pIter );
PIDBIterator_next( pIter ))
{
DescriptionMap *pDesc = (DescriptionMap *)PIDBIterator_current( pIter, 0 );
assert( pDesc );
ofs << pDesc->sFileName << "|" << pDesc->sDescription << "\n";
PI_DELETE( pDesc );
};
};
PIDBIterator_delete( pIter );
HTTPCore_releaseCachedFile( pFInfo );
return 1;
};
/* ---
returns 1 if the file has been modified, i.e. needs to be completely reloaded
--- */
int checkIfRange( const char *szIfRangeHeader, PIFInfo *pFInfo )
{
// no If-Range header, continue
if (!szIfRangeHeader) return 0;
/* --- read file Last-Modified --- */
time_t tTmp = PIFInfo_getLastModified( pFInfo );
PIPlatform_beforeUnsafeBlock();
struct tm *pLM = gmtime( &tTmp );
if ( !pLM )
{
PIPlatform_afterUnsafeBlock();
return 1;
};
struct tm tLM;
memcpy( &tLM, pLM, sizeof( struct tm ) );
PIPlatform_afterUnsafeBlock();
/* --- read If-Modified-Since time --- */
struct tm tIMS;
if ( HTTPUtil_readTime( szIfRangeHeader, &tIMS ) )
{ return 1; };
/* ---
Compare modification dates
--- */
return (
tLM.tm_sec != tIMS.tm_sec ||
tLM.tm_min != tIMS.tm_min ||
tLM.tm_hour != tIMS.tm_hour ||
tLM.tm_mday != tIMS.tm_mday ||
tLM.tm_mon != tIMS.tm_mon ||
tLM.tm_year != tIMS.tm_year
);
}
/* ---
Implementation of HTTP methods GET and HEAD (send headers and file)
--- */
int Get_Head( PIHTTP &tPIHTTP, int iMethod, const char *szRangeHeader )
{
PIDB *pQ = tPIHTTP.pRequestDB;
PIDB *pR = tPIHTTP.pResponseDB;
/* ---
Get file object
--- */
const char *pPath = (const char *)PIDB_lookup( pR, PIDBTYPE_STRING,
pFKPath, PIDBFLAG_FASTKEY );
PIFInfo *pFInfo = HTTPCore_getCachedFile( pPath );
if ( !pFInfo ) return PIAPI_ERROR;
/* ---
This file must exist and be a regular file
--- */
if ( !PIFInfo_exists( pFInfo ) ||
!PIFInfo_isRegular( pFInfo ) )
{
HTTPCore_logError( &tPIHTTP, "SendFile: File with \
path '%s' does not exist or is not a regular file.", pPath );
HTTPCore_releaseCachedFile( pFInfo );
return HTTPUtil_doHTTPError( &tPIHTTP, ST_INTERNALERROR );
};
/* --- ranges (RFC2616, 14.35) --- */
unsigned int uiFrom = 0;
unsigned int uiTo = 0;
int iFileSize = PIFInfo_getSize( pFInfo );
int iRange = 0;
if ( iAllowRanges )
{
// Tell the client, that this handler supports ranges
PIDB_replace( pR, PIDBTYPE_RFC822, KEY_HTTP_ACCEPTRANGES, (void *)KEY_HTTP_BYTES, 0 );
const char *szIfRangeHeader = (const char *)PIDB_lookup( pQ, PIDBTYPE_RFC822,
pFKIfRange, PIDBFLAG_FASTKEY);
// extract range values and perform some sanity checks
if (szRangeHeader)
{
// This handler supports only byte ranges
if ( int i = PIUtil_strncmpi(szRangeHeader, KEY_HTTP_BYTES, sizeof(KEY_HTTP_BYTES)-1)
|| strchr(szRangeHeader, ',') )
{
HTTPCore_logError( &tPIHTTP, "SendFile: Only simple byte ranges are supported.");
PIDB_replace( pR, PIDBTYPE_RFC822, KEY_HTTP_CONTENTRANGE, (void *)"*", 0 );
HTTPCore_releaseCachedFile( pFInfo );
return HTTPUtil_doHTTPError( &tPIHTTP, ST_RANGENOTSATISFIABLE );
}
// search for '='
const char *szFrom = strchr(szRangeHeader, '=');
if (szFrom) szFrom++;
// szFrom now points to the 1st char beyond the '=',
// i.e. can contain only '-' or digits
if (!szFrom || strcspn(szFrom, "-1234567890"))
{
HTTPCore_logError( &tPIHTTP, "SendFile: Invalid range specifier.");
PIDB_replace( pR, PIDBTYPE_RFC822, KEY_HTTP_CONTENTRANGE, (void *)"*", 0 );
HTTPCore_releaseCachedFile( pFInfo );
return HTTPUtil_doHTTPError( &tPIHTTP, ST_RANGENOTSATISFIABLE );
}
// search for '-'
const char *szTo = strchr(szRangeHeader, '-');
// szTo can contain only '-' or digits
if (!szTo || strcspn(szTo, "-1234567890"))
{
HTTPCore_logError( &tPIHTTP, "SendFile: Invalid range specifier.");
PIDB_replace( pR, PIDBTYPE_RFC822, KEY_HTTP_CONTENTRANGE, (void *)"*", 0 );
HTTPCore_releaseCachedFile( pFInfo );
return HTTPUtil_doHTTPError( &tPIHTTP, ST_RANGENOTSATISFIABLE );
}
// this means a range spec. of (final) 'bytes=-nnnn'
if (szFrom == szTo)
{
uiFrom = iFileSize-1 - atol(szTo++);
uiTo = iFileSize-1;
}
// this means a range spec. of (final) 'bytes=nnnn-'
else if (strlen(szTo) == 1)
{
uiFrom = atol(szFrom);
uiTo = iFileSize-1;
}
// this means a range spec. of 'bytes=nnnn-mmmm'
else
{
uiFrom = atol(szFrom);
uiTo = atol(szTo++);
}
iRange = 1 + uiTo - uiFrom;
// Sanity check of the range values
if (iRange < 1 || iRange > iFileSize || uiTo > iFileSize-1 )
{
HTTPCore_logError( &tPIHTTP, "SendFile: Invalid range specifier.");
PIDB_replace( pR, PIDBTYPE_RFC822, KEY_HTTP_CONTENTRANGE, (void *)"*", 0 );
HTTPCore_releaseCachedFile( pFInfo );
return HTTPUtil_doHTTPError( &tPIHTTP, ST_RANGENOTSATISFIABLE );
}
// evaluate IfRangeHeader, we do this after the sanity check
// to be sure the Range header is valid nevertheless
if ( checkIfRange( szIfRangeHeader, pFInfo ) )
{
// file is modified, i.e. the whole file mus be re-sent
uiFrom = 0;
uiTo = iFileSize;
iRange = 1 + uiTo - uiFrom;
}
enum { BUF_SIZE=63 };
char szBuf[BUF_SIZE+1];
sprintf(szBuf, "bytes %d-%d/%d",uiFrom, uiTo, iFileSize);
PIDB_replace( pR, PIDBTYPE_RFC822, KEY_HTTP_CONTENTRANGE, szBuf, 0 );
}
else
{
// If-Range requires a Range header
if (szIfRangeHeader)
{
HTTPCore_logError( &tPIHTTP, "SendFile: If-Range header without Range received.");
HTTPCore_releaseCachedFile( pFInfo );
return HTTPUtil_doHTTPError( &tPIHTTP, ST_BADREQUEST );
}
}
}
else
{
// Tell the client, that this handler doesn't support ranges
PIDB_replace( pR, PIDBTYPE_RFC822, KEY_HTTP_ACCEPTRANGES, (void *)KEY_HTTP_NONE, 0 );
// Can't handle range request, the support for ranges has been switched off
// Exception: the whole resource has been requested (bytes=0-)
if (szRangeHeader && PIUtil_stricmp(szRangeHeader, "bytes=0-"))
{
HTTPCore_logError( &tPIHTTP, "SendFile: This handler is not configured to support ranges.");
HTTPCore_releaseCachedFile( pFInfo );
return HTTPUtil_doHTTPError( &tPIHTTP, ST_NOTIMPLEMENTED );
}
}
/* ---
Get the header
--- */
enum { PATTERN_BUF_SIZE=1024 };
char szHeader[PATTERN_BUF_SIZE];
char *pHeader = 0;
int iHeaderLen = 0;
if ( pHeaderPattern )
{
pHeader = ( iMethod==MD_GET ) ? szHeader : 0;
iHeaderLen = Pi3Expression_write( pHeaderPattern, &tPIHTTP, 0, pHeader, PATTERN_BUF_SIZE );
if ( iHeaderLen>PATTERN_BUF_SIZE && pHeader )
{
pHeader = PI_NEW( char[iHeaderLen] );
Pi3Expression_write( pHeaderPattern, &tPIHTTP, 0, pHeader, iHeaderLen );
};
};
/* ---
Get the footer
--- */
char szFooter[PATTERN_BUF_SIZE];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -