From 75f4a44b58d3826c63a7dec85a45be9de66af143 Mon Sep 17 00:00:00 2001 From: Francesco Saverio Giudice Date: Mon, 6 Apr 2009 22:42:34 +0000 Subject: [PATCH] 2009-04-07 00:41 UTC+0200 Francesco Saverio Giudice (info/at/fsgiudice.com) * harbour/contrib/examples/uhttpd/uhttpd.prg + Added _HTTP_RESPONSE headers * Renamed defineServerAdresses() function to defineServer() as now define all _SERVER variables * ParseRequest() now uses uhttpd_SplitUrl() to define URI parts * Host now is defined according with RFC2616 * HRB session now is defined as UHTTPD-SESSION to avoid collision with SESSION in cookies ! uhttpd_AddHeader() renamed to uhttpd_SetHeader() to be conformed to RFC2616 that wants that a header variable is defined only once. See cookie.prg changes. Headers now are contained in a Hash instead of an array * fixed as per above also uhttpd_GetHeader() / uhttpd_DelHeader() * harbour/contrib/examples/uhttpd/session.prg * uhttpd_AddHeader() -> uhttpd_SetHeader() * harbour/contrib/examples/uhttpd/cgifunc.prg * in function uhttpd_SplitUrl() added URI hash field + harbour/contrib/examples/uhttpd/cookie.prg * To conform to RFC2616 now if Set-Cookie has more than one value than is created a comma separated list of cookie-name=cookie-value * uhttpd_AddHeader() -> uhttpd_SetHeader() * harbour/contrib/examples/uhttpd/modules/info.prg + Added _HTTP_RESPONSE display * harbour/contrib/examples/uhttpd/modules/cookie.prg * harbour/contrib/examples/uhttpd/modules/showcounter.prg * harbour/contrib/examples/uhttpd/modules/tableservletdb.prg * uhttpd_AddHeader() -> uhttpd_SetHeader() --- harbour/ChangeLog | 32 +++ harbour/contrib/examples/uhttpd/cgifunc.prg | 11 +- harbour/contrib/examples/uhttpd/cookie.prg | 27 +- .../examples/uhttpd/modules/cookie.prg | 2 +- .../contrib/examples/uhttpd/modules/info.prg | 20 +- .../examples/uhttpd/modules/showcounter.prg | 10 +- .../uhttpd/modules/tableservletdb.prg | 8 +- harbour/contrib/examples/uhttpd/session.prg | 24 +- harbour/contrib/examples/uhttpd/uhttpd.prg | 235 +++++++++++++----- 9 files changed, 268 insertions(+), 101 deletions(-) diff --git a/harbour/ChangeLog b/harbour/ChangeLog index 7fe21e63c0..8939255d95 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -8,6 +8,38 @@ 2009-12-31 13:59 UTC+0100 Foo Bar (foo.bar foobar.org) */ +2009-04-07 00:41 UTC+0200 Francesco Saverio Giudice (info/at/fsgiudice.com) + * harbour/contrib/examples/uhttpd/uhttpd.prg + + Added _HTTP_RESPONSE headers + * Renamed defineServerAdresses() function to defineServer() + as now define all _SERVER variables + * ParseRequest() now uses uhttpd_SplitUrl() to define URI parts + * Host now is defined according with RFC2616 + * HRB session now is defined as UHTTPD-SESSION to avoid collision with + SESSION in cookies + ! uhttpd_AddHeader() renamed to uhttpd_SetHeader() to be conformed to + RFC2616 that wants that a header variable is defined only once. + See cookie.prg changes. + Headers now are contained in a Hash instead of an array + * fixed as per above also uhttpd_GetHeader() / uhttpd_DelHeader() + + * harbour/contrib/examples/uhttpd/session.prg + * uhttpd_AddHeader() -> uhttpd_SetHeader() + * harbour/contrib/examples/uhttpd/cgifunc.prg + * in function uhttpd_SplitUrl() added URI hash field + + harbour/contrib/examples/uhttpd/cookie.prg + * To conform to RFC2616 now if Set-Cookie has more than one value than + is created a comma separated list of cookie-name=cookie-value + * uhttpd_AddHeader() -> uhttpd_SetHeader() + + * harbour/contrib/examples/uhttpd/modules/info.prg + + Added _HTTP_RESPONSE display + + * harbour/contrib/examples/uhttpd/modules/cookie.prg + * harbour/contrib/examples/uhttpd/modules/showcounter.prg + * harbour/contrib/examples/uhttpd/modules/tableservletdb.prg + * uhttpd_AddHeader() -> uhttpd_SetHeader() + 2009-04-06 23:38 UTC+0200 Viktor Szakats (harbour.01 syenar hu) * utils/hbmk2/hbmk2.prg ! Fixed to properly set errorlevels. For some reason I thought diff --git a/harbour/contrib/examples/uhttpd/cgifunc.prg b/harbour/contrib/examples/uhttpd/cgifunc.prg index 857b089f87..5ddeed8710 100644 --- a/harbour/contrib/examples/uhttpd/cgifunc.prg +++ b/harbour/contrib/examples/uhttpd/cgifunc.prg @@ -95,7 +95,7 @@ FUNCTION uhttpd_GetVars( cFields, cSeparator ) // with same name, then I will change it to an array IF ( hb_HPos( hHashVars, cName ) ) > 0 IF !HB_ISARRAY( hHashVars[ cName ] ) - // Transoform it to array + // Transform it to array hHashVars[ cName ] := { hHashVars[ cName ] } ENDIF aAdd( hHashVars[ cName ], xValue ) @@ -137,11 +137,13 @@ FUNCTION uhttpd_SplitUrl( cUrl ) LOCAL hUrl := hb_Hash() LOCAL nPos, cTemp, cUserNamePassword, cHostnamePort LOCAL cProto, cHost, cPort, nPort, cUser, cPass, cPath, cQuery, cFragment + LOCAL cUri // Prevents case matching hb_HSetCaseMatch( hUrl, FALSE ) cTemp := cUrl + cUri := "" // Starting with // http://[username:password@]hostname[:port][/path[/file[.ext]][?arg1=[value][&arg2=[value]]][#anchor]] @@ -156,6 +158,8 @@ FUNCTION uhttpd_SplitUrl( cUrl ) cProto := "" ENDIF + cUri += cProto + IIF( !Empty( cProto ), "://", "" ) + // Now we have: // [username:password@]hostname[:port][/path[/file[.ext]][?arg1=[value][&arg2=[value]]][#anchor]] @@ -212,6 +216,8 @@ FUNCTION uhttpd_SplitUrl( cUrl ) // Now we have: // hostname[:port][/path[/file[.ext]] + cUri += cTemp + // Search for Path part using / char from right nPos := RAt( "/", cTemp ) IF nPos > 0 @@ -225,7 +231,7 @@ FUNCTION uhttpd_SplitUrl( cUrl ) ENDIF // Now we have: - // hostname[:port][/path[/file[.ext]] + // hostname[:port] cHostnamePort := cTemp @@ -252,6 +258,7 @@ FUNCTION uhttpd_SplitUrl( cUrl ) hb_hSet( hUrl, "PATH" , cPath ) hb_hSet( hUrl, "QUERY" , cQuery ) hb_hSet( hUrl, "FRAGMENT", cFragment ) + hb_hSet( hUrl, "URI" , cURI ) // Prevents externals to add something else to this Hash hb_HSetAutoAdd( hUrl, FALSE ) diff --git a/harbour/contrib/examples/uhttpd/cookie.prg b/harbour/contrib/examples/uhttpd/cookie.prg index d3f7db580c..301571622d 100644 --- a/harbour/contrib/examples/uhttpd/cookie.prg +++ b/harbour/contrib/examples/uhttpd/cookie.prg @@ -67,6 +67,7 @@ RETURN uhttpd_Cookie():New( cDomain, cPath, nExpireDays, nExpireSecs ) CLASS uhttpd_Cookie // Data for cookies + DATA aCookies INIT {} // Using an array to mantain order DATA cDomain DATA cPath INIT "/" DATA cExpire @@ -96,7 +97,7 @@ METHOD SetCookieDefaults( cDomain, cPath, nExpireDays, nExpireSecs ) CLASS uhttp RETURN NIL METHOD SetCookie( cCookieName, xValue, cDomain, cPath, cExpires, lSecure, lHttpOnly ) CLASS uhttpd_Cookie - LOCAL cStr + LOCAL cStr, nPos, nCookies DEFAULT cDomain TO ::cDomain DEFAULT cPath TO ::cPath @@ -105,7 +106,26 @@ METHOD SetCookie( cCookieName, xValue, cDomain, cPath, cExpires, lSecure, lHttpO ::lHttpOnly := lHttpOnly - cStr := cCookieName + "=" + uhttpd_UrlEncode( hb_cStr( xValue ) ) + IF !Empty( xValue ) + // Search if a cookie already exists + // case sensitive + IF ( nPos := aScan( ::aCookies, {|e| e[ 1 ] == cCookieName } ) ) > 0 + ::aCookies[ nPos ][ 2 ] := uhttpd_UrlEncode( hb_cStr( xValue ) ) + ELSE + aAdd( ::aCookies, { cCookieName, uhttpd_UrlEncode( hb_cStr( xValue ) ) } ) + ENDIF + ELSE + IF ( nPos := aScan( ::aCookies, {|e| e[ 1 ] == cCookieName } ) ) > 0 + hb_aDel( ::aCookies, nPos, .T. ) + ENDIF + ENDIF + + // Rebuild cookie string as per RFC2616 (comma separated list) + cStr := "" + nCookies := Len( ::aCookies ) + aEval( ::aCookies, {|e, i| cStr += e[ 1 ] + "=" + e[ 2 ] + IIF( i < nCookies, ",", "" ) } ) + + //cStr := cCookieName + "=" + uhttpd_UrlEncode( hb_cStr( xValue ) ) IF cDomain <> NIL cStr += "; domain=" + cDomain @@ -121,7 +141,8 @@ METHOD SetCookie( cCookieName, xValue, cDomain, cPath, cExpires, lSecure, lHttpO ENDIF // Send the header - uhttpd_AddHeader( "Set-Cookie", cStr, FALSE ) + //uhttpd_SetHeader( "Set-Cookie", cStr, FALSE ) + uhttpd_SetHeader( "Set-Cookie", cStr ) RETURN NIL diff --git a/harbour/contrib/examples/uhttpd/modules/cookie.prg b/harbour/contrib/examples/uhttpd/modules/cookie.prg index 6a796a239f..50a58d2279 100644 --- a/harbour/contrib/examples/uhttpd/modules/cookie.prg +++ b/harbour/contrib/examples/uhttpd/modules/cookie.prg @@ -108,7 +108,7 @@ Pressing button you will redirect to /info page. Look at COOKIE values. // Set a simple cookie oCookie := uhttpd_CookieNew( "localhost", "/", 1, 0 ) oCookie:SetCookie( "mycookie", cCookie ) - uhttpd_AddHeader( "Location", "/info" ) + uhttpd_SetHeader( "Location", "/info" ) //uhttpd_Write( "cookie set Go to info page" ) RETURN NIL ENDIF diff --git a/harbour/contrib/examples/uhttpd/modules/info.prg b/harbour/contrib/examples/uhttpd/modules/info.prg index 664647bd7b..78eadf56f2 100644 --- a/harbour/contrib/examples/uhttpd/modules/info.prg +++ b/harbour/contrib/examples/uhttpd/modules/info.prg @@ -59,7 +59,7 @@ #include "common.ch" #include "hbclass.ch" -MEMVAR _SERVER, _REQUEST, _GET, _POST, _COOKIE, _SESSION, _HTTP_REQUEST +MEMVAR _SERVER, _REQUEST, _GET, _POST, _COOKIE, _SESSION, _HTTP_REQUEST, _HTTP_RESPONSE FUNCTION HRBMAIN() LOCAL cHtml @@ -76,21 +76,23 @@ STATIC FUNCTION ShowServerInfo() //cHtml += "

If it is first time you see this page reload it to see cookies

" cHtml += '

Return to Main Page

' - cHtml += DisplayVars( _Server , "SERVER Vars" ) + cHtml += DisplayVars( _Server , "SERVER Vars" ) cHtml += "
" - cHtml += DisplayVars( _HTTP_REQUEST , "HTTP Headers" ) + cHtml += DisplayVars( _HTTP_REQUEST , "HTTP Request Headers" ) cHtml += "
" - cHtml += DisplayVars( _Get , "GET Vars" ) + cHtml += DisplayVars( _HTTP_RESPONSE, "HTTP Response Headers" ) cHtml += "
" - cHtml += DisplayVars( _Post , "POST Vars" ) + cHtml += DisplayVars( _Get , "GET Vars" ) cHtml += "
" - cHtml += DisplayVars( _Cookie , "COOKIE Vars" ) + cHtml += DisplayVars( _Post , "POST Vars" ) cHtml += "
" - //cHtml += DisplayVars( _Files , "FILE Vars" ) + cHtml += DisplayVars( _Cookie , "COOKIE Vars" ) + cHtml += "
" + //cHtml += DisplayVars( _Files , "FILE Vars" ) //cHtml += "
" - cHtml += DisplayVars( _Request, "REQUEST Vars" ) + cHtml += DisplayVars( _Request , "REQUEST Vars" ) cHtml += "
" - cHtml += DisplayVars( _Session, "SESSION Vars" ) + cHtml += DisplayVars( _Session , "SESSION Vars" ) cHtml += "
" // Set a simple cookie diff --git a/harbour/contrib/examples/uhttpd/modules/showcounter.prg b/harbour/contrib/examples/uhttpd/modules/showcounter.prg index e4a16c9254..50fefd138e 100644 --- a/harbour/contrib/examples/uhttpd/modules/showcounter.prg +++ b/harbour/contrib/examples/uhttpd/modules/showcounter.prg @@ -78,19 +78,19 @@ FUNCTION HRBMAIN() cHtml := CreateCounter( AllTrim( Str( Val( _REQUEST[ "w" ] ) ) ) ) //hb_ToOutDebug( hb_sprintf( "CreateCounter = %s", cHtml ) ) IF !Empty( cHtml ) - uhttpd_AddHeader( "Content-Type", "image/gif" ) - uhttpd_AddHeader( "Pragma", "no-cache" ) - uhttpd_AddHeader( "Content-Disposition", "inline; filename=counter" + LTrim( Str( hb_randomint( 100 ) ) ) + ".gif" ) + uhttpd_SetHeader( "Content-Type", "image/gif" ) + uhttpd_SetHeader( "Pragma", "no-cache" ) + uhttpd_SetHeader( "Content-Disposition", "inline; filename=counter" + LTrim( Str( hb_randomint( 100 ) ) ) + ".gif" ) uhttpd_Write( cHtml ) ELSE - uhttpd_AddHeader( "Content-Type", "text/html" ) + uhttpd_SetHeader( "Content-Type", "text/html" ) uhttpd_Write( "

Error: No image created

" ) ENDIF ELSE - uhttpd_AddHeader( "Content-Type", "text/html" ) + uhttpd_SetHeader( "Content-Type", "text/html" ) uhttpd_Write( "

Error: no parameters passed

" ) ENDIF diff --git a/harbour/contrib/examples/uhttpd/modules/tableservletdb.prg b/harbour/contrib/examples/uhttpd/modules/tableservletdb.prg index fe5ab8ae7f..495abc5b92 100644 --- a/harbour/contrib/examples/uhttpd/modules/tableservletdb.prg +++ b/harbour/contrib/examples/uhttpd/modules/tableservletdb.prg @@ -111,16 +111,16 @@ FUNCTION HRBMAIN() IF !Empty( cXml ) - uhttpd_AddHeader("Content-Type", "text/xml") + uhttpd_SetHeader( "Content-Type", "text/xml" ) // cache control - uhttpd_AddHeader( "Cache-Control", "no-cache, must-revalidate" ) - uhttpd_AddHeader( "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" ) + uhttpd_SetHeader( "Cache-Control", "no-cache, must-revalidate" ) + uhttpd_SetHeader( "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" ) uhttpd_Write( cXml ) ELSE - uhttpd_AddHeader("Content-Type", "text/xml") + uhttpd_SetHeader("Content-Type", "text/xml") uhttpd_Write( '' ) uhttpd_Write( 'No Data' ) diff --git a/harbour/contrib/examples/uhttpd/session.prg b/harbour/contrib/examples/uhttpd/session.prg index 6ccd4da1ba..fa02e571dd 100644 --- a/harbour/contrib/examples/uhttpd/session.prg +++ b/harbour/contrib/examples/uhttpd/session.prg @@ -844,23 +844,23 @@ METHOD SendCacheLimiter() CLASS uhttpd_Session LOCAL dDate DO CASE CASE ::cCache_Limiter == 'nocache' - //uhttpd_AddHeader( 'Expires', 'Thu, 19 Nov 1981 08:52:00 GMT' ) - uhttpd_AddHeader( 'Expires', uhttpd_DateToGMT( ,,-1, ) ) - uhttpd_AddHeader( 'Cache-Control', 'no-cache' ) - //uhttpd_AddHeader("Cache-Control", "no-store, no-cache, must-revalidate") // HTTP/1.1 - //uhttpd_AddHeader("Cache-Control", "post-check=0, pre-check=0", FALSE ) - uhttpd_AddHeader( 'Pragma', 'no-cache' ) + //uhttpd_SetHeader( 'Expires', 'Thu, 19 Nov 1981 08:52:00 GMT' ) + uhttpd_SetHeader( 'Expires', uhttpd_DateToGMT( ,,-1, ) ) + uhttpd_SetHeader( 'Cache-Control', 'no-cache' ) + //uhttpd_SetHeader("Cache-Control", "no-store, no-cache, must-revalidate") // HTTP/1.1 + //uhttpd_SetHeader("Cache-Control", "post-check=0, pre-check=0", FALSE ) + uhttpd_SetHeader( 'Pragma', 'no-cache' ) CASE ::cCache_Limiter == 'private' - uhttpd_AddHeader( 'Expires', 'Thu, 19 Nov 1981 08:52:00 GMT' ) - uhttpd_AddHeader( 'Cache-Control', 'private, max-age=' + LTrim( Str( ::nCache_Expire * 60 ) ) ) + uhttpd_SetHeader( 'Expires', 'Thu, 19 Nov 1981 08:52:00 GMT' ) + uhttpd_SetHeader( 'Cache-Control', 'private, max-age=' + LTrim( Str( ::nCache_Expire * 60 ) ) ) IF hb_FGetDateTime( hb_argv(0), @dDate ) - uhttpd_AddHeader( 'Last-Modified', uhttpd_DateToGMT( dDate ) ) + uhttpd_SetHeader( 'Last-Modified', uhttpd_DateToGMT( dDate ) ) ENDIF CASE ::cCache_Limiter == 'public' - uhttpd_AddHeader( 'Expires', uhttpd_DateToGMT( ,,, ::nCache_Expire * 60 ) ) - uhttpd_AddHeader( 'Cache-Control', 'public, max-age=' + LTrim( Str( ::nCache_Expire * 60 ) ) ) + uhttpd_SetHeader( 'Expires', uhttpd_DateToGMT( ,,, ::nCache_Expire * 60 ) ) + uhttpd_SetHeader( 'Cache-Control', 'public, max-age=' + LTrim( Str( ::nCache_Expire * 60 ) ) ) IF hb_FGetDateTime( hb_argv(0), @dDate ) - uhttpd_AddHeader( 'Last-Modified', uhttpd_DateToGMT( dDate ) ) + uhttpd_SetHeader( 'Last-Modified', uhttpd_DateToGMT( dDate ) ) ENDIF OTHERWISE uhttpd_Die( "ERROR: Caching method " + ::cCache_Limiter + " not implemented." ) diff --git a/harbour/contrib/examples/uhttpd/uhttpd.prg b/harbour/contrib/examples/uhttpd/uhttpd.prg index ac454ed87b..af8897d12c 100644 --- a/harbour/contrib/examples/uhttpd/uhttpd.prg +++ b/harbour/contrib/examples/uhttpd/uhttpd.prg @@ -230,9 +230,9 @@ STATIC s_hHandlers := { ; STATIC s_hScriptAliases := { => } STATIC s_hAliases := { => } -THREAD STATIC t_cResult, t_nStatusCode, t_aHeader, t_cErrorMsg, t_oSession +THREAD STATIC t_cResult, t_nStatusCode, /*t_aHeader,*/ t_cErrorMsg, t_oSession -MEMVAR _SERVER, _GET, _POST, _COOKIE, _SESSION, _REQUEST, _HTTP_REQUEST, m_cPost +MEMVAR _SERVER, _GET, _POST, _COOKIE, _SESSION, _REQUEST, _HTTP_REQUEST, _HTTP_RESPONSE, m_cPost ANNOUNCE ERRORSYS @@ -567,6 +567,7 @@ FUNCTION MAIN( ... ) IF hb_InetErrorCode( hListen ) != 0 ? "Bind Error" + WAIT ELSE s_nLocalPort := hb_InetPort( hListen ) @@ -880,7 +881,7 @@ STATIC FUNCTION ProcessConnection() LOCAL nMsecs, nParseTime, nPos, nThreadID LOCAL lQuitRequest := FALSE - PRIVATE _SERVER, _GET, _POST, _COOKIE, _SESSION, _REQUEST, _HTTP_REQUEST, m_cPost + PRIVATE _SERVER, _GET, _POST, _COOKIE, _SESSION, _REQUEST, _HTTP_REQUEST, _HTTP_RESPONSE, m_cPost nThreadId := hb_threadID() @@ -955,6 +956,8 @@ STATIC FUNCTION ProcessConnection() BEGIN SEQUENCE + cRequest := NIL + /* receive query */ nLen := readRequest( hSocket, @cRequest ) @@ -968,22 +971,23 @@ STATIC FUNCTION ProcessConnection() ELSEIF nLen == 0 /* connection closed */ ELSE - //hb_ToOutDebug( "cRequest -- INIZIO --\n\r%s\n\rcRequest -- FINE --\n\r", cRequest ) + //hb_ToOutDebug( "cRequest -- BEGIN --\n\r%s\n\rcRequest -- END --\n\r", cRequest ) _SERVER := HB_IHASH(); _GET := HB_IHASH(); _POST := HB_IHASH(); _COOKIE := HB_IHASH() - _SESSION := HB_IHASH(); _REQUEST := HB_IHASH(); _HTTP_REQUEST := HB_IHASH(); m_cPost := NIL + _SESSION := HB_IHASH(); _REQUEST := HB_IHASH(); _HTTP_REQUEST := HB_IHASH(); _HTTP_RESPONSE := HB_IHASH() + m_cPost := NIL t_cResult := "" - t_aHeader := {} + //t_aHeader := {} t_nStatusCode := 200 t_cErrorMsg := "" - defineServerAdresses( hSocket ) + defineServer( hSocket ) IF ParseRequest( cRequest ) - //hb_ToOutDebug( "_SERVER = %s,\n\r _GET = %s,\n\r _POST = %s,\n\r _REQUEST = %s,\n\r _HTTP_REQUEST = %s\n\r", hb_ValToExp( _SERVER ), hb_ValToExp( _GET ), hb_ValToExp( _POST ), hb_ValToExp( _REQUEST ), hb_ValToExp( _HTTP_REQUEST ) ) + //hb_ToOutDebug( "_SERVER = %s,\n\r _GET = %s,\n\r _POST = %s,\n\r _REQUEST = %s,\n\r _HTTP_REQUEST = %s,\n\r _HTTP_RESPONSE = %s\n\r", hb_ValToExp( _SERVER ), hb_ValToExp( _GET ), hb_ValToExp( _POST ), hb_ValToExp( _REQUEST ), hb_ValToExp( _HTTP_REQUEST ), hb_ValToExp( _HTTP_RESPONSE ) ) cSend := uproc_default() ELSE - uhttpd_SetStatusCode( 400 ) + //uhttpd_SetStatusCode( 400 ) cSend := MakeResponse() ENDIF @@ -994,7 +998,7 @@ STATIC FUNCTION ProcessConnection() WriteToLog( cRequest ) // Destroy PRIVATE VARIABLES - _SERVER := _GET := _POST := _COOKIE := _SESSION := _REQUEST := _HTTP_REQUEST := m_cPost := NIL + _SERVER := _GET := _POST := _COOKIE := _SESSION := _REQUEST := _HTTP_REQUEST := _HTTP_RESPONSE := m_cPost := NIL ENDIF @@ -1043,7 +1047,7 @@ STATIC FUNCTION ServiceConnection() LOCAL nError := 500013 LOCAL lQuitRequest := FALSE - PRIVATE _SERVER, _GET, _POST, _COOKIE, _SESSION, _REQUEST, _HTTP_REQUEST, m_cPost + PRIVATE _SERVER, _GET, _POST, _COOKIE, _SESSION, _REQUEST, _HTTP_REQUEST, _HTTP_RESPONSE, m_cPost ErrorBlock( { | oError | uhttpd_DefError( oError ) } ) @@ -1125,16 +1129,17 @@ STATIC FUNCTION ServiceConnection() //hb_ToOutDebug( "cRequest -- INIZIO --\n\r%s\n\rcRequest -- FINE --\n\r", cRequest ) _SERVER := HB_IHASH(); _GET := HB_IHASH(); _POST := HB_IHASH(); _COOKIE := HB_IHASH() - _SESSION := HB_IHASH(); _REQUEST := HB_IHASH(); _HTTP_REQUEST := HB_IHASH(); m_cPost := NIL + _SESSION := HB_IHASH(); _REQUEST := HB_IHASH(); _HTTP_REQUEST := HB_IHASH(); _HTTP_RESPONSE := HB_IHASH() + m_cPost := NIL t_cResult := "" - t_aHeader := {} + //t_aHeader := {} t_nStatusCode := 200 t_cErrorMsg := "" - defineServerAdresses( hSocket ) + defineServer( hSocket ) IF ParseRequest( cRequest ) - //hb_ToOutDebug( "_SERVER = %s,\n\r _GET = %s,\n\r _POST = %s,\n\r _REQUEST = %s,\n\r _HTTP_REQUEST = %s\n\r", hb_ValToExp( _SERVER ), hb_ValToExp( _GET ), hb_ValToExp( _POST ), hb_ValToExp( _REQUEST ), hb_ValToExp( _HTTP_REQUEST ) ) + //hb_ToOutDebug( "_SERVER = %s,\n\r _GET = %s,\n\r _POST = %s,\n\r _REQUEST = %s,\n\r _HTTP_REQUEST = %s,\n\r _HTTP_RESPONSE = %s\n\r", hb_ValToExp( _SERVER ), hb_ValToExp( _GET ), hb_ValToExp( _POST ), hb_ValToExp( _REQUEST ), hb_ValToExp( _HTTP_REQUEST ), hb_ValToExp( _HTTP_RESPONSE ) ) define_Env( _SERVER ) ENDIF // Error page served @@ -1146,7 +1151,7 @@ STATIC FUNCTION ServiceConnection() WriteToLog( cRequest ) // Destroy PRIVATE VARIABLES - _SERVER := _GET := _POST := _COOKIE := _SESSION := _REQUEST := _HTTP_REQUEST := m_cPost := NIL + _SERVER := _GET := _POST := _COOKIE := _SESSION := _REQUEST := _HTTP_REQUEST := _HTTP_RESPONSE := m_cPost := NIL ENDIF @@ -1191,6 +1196,7 @@ STATIC FUNCTION ServiceConnection() STATIC FUNCTION ParseRequest( cRequest ) LOCAL aRequest, aLine, nI, nJ, cI LOCAL cReq, aVal, cFields, hVars + LOCAL hUrl // RFC2616 aRequest := uhttpd_split( CR_LF, cRequest ) @@ -1202,6 +1208,8 @@ STATIC FUNCTION ParseRequest( cRequest ) IF LEN( aLine ) != 3 .OR. ; ( aLine[1] != "GET" .AND. aLine[1] != "POST" ) .OR. ; // Sorry, we support GET and POST only LEFT( aLine[3], 5 ) != "HTTP/" + // Set status code + t_nStatusCode := 501 // Not Implemented RETURN .F. ENDIF @@ -1210,25 +1218,20 @@ STATIC FUNCTION ParseRequest( cRequest ) _SERVER[ "REQUEST_URI" ] := aLine[2] _SERVER[ "SERVER_PROTOCOL" ] := aLine[3] - IF ( nI := AT( "?", _SERVER["REQUEST_URI"] ) ) > 0 - _SERVER[ "SCRIPT_NAME" ] := LEFT( _SERVER["REQUEST_URI"], nI - 1) - _SERVER[ "QUERY_STRING" ] := SUBSTR( _SERVER["REQUEST_URI"], nI + 1) + hUrl := uhttpd_SplitUrl( _SERVER[ "REQUEST_URI" ] ) + + _SERVER[ "SCRIPT_NAME" ] := hUrl[ "URI" ] + _SERVER[ "QUERY_STRING" ] := hUrl[ "QUERY" ] + + /* + IF ( nI := AT( "?", _SERVER[ "REQUEST_URI" ] ) ) > 0 + _SERVER[ "SCRIPT_NAME" ] := LEFT( _SERVER[ "REQUEST_URI" ], nI - 1) + _SERVER[ "QUERY_STRING" ] := SUBSTR( _SERVER[ "REQUEST_URI" ], nI + 1) ELSE - _SERVER[ "SCRIPT_NAME" ] := _SERVER["REQUEST_URI"] + _SERVER[ "SCRIPT_NAME" ] := _SERVER[ "REQUEST_URI" ] _SERVER[ "QUERY_STRING" ] := "" ENDIF - - _SERVER[ "HTTP_ACCEPT" ] := "" - _SERVER[ "HTTP_ACCEPT_CHARSET" ] := "" - _SERVER[ "HTTP_ACCEPT_ENCODING" ] := "" - _SERVER[ "HTTP_ACCEPT_LANGUAGE" ] := "" - _SERVER[ "HTTP_CONNECTION" ] := "" - _SERVER[ "HTTP_HOST" ] := "" - _SERVER[ "HTTP_KEEP_ALIVE" ] := "" - _SERVER[ "HTTP_REFERER" ] := "" - _SERVER[ "HTTP_USER_AGENT" ] := "" - _SERVER[ "HTTP_CACHE_CONTROL" ] := "" - _SERVER[ "HTTP_COOKIE" ] := "" + */ FOR nI := 2 TO LEN( aRequest ) IF aRequest[nI] == ""; EXIT @@ -1248,8 +1251,9 @@ STATIC FUNCTION ParseRequest( cRequest ) _SERVER[ "HTTP_" + STRTRAN( UPPER( LEFT( aRequest[nI], nJ - 1 ) ), "-", "_" ) ] := cI EXIT CASE "HOST" - aVal := uhttpd_split( ":", aRequest[ nI ] ) - _SERVER[ "HTTP_" + STRTRAN( UPPER( aVal[ 1 ] ), "-", "_")] := AllTrim( aVal[ 2 ] ) + //aVal := uhttpd_split( ":", aRequest[ nI ] ) + //_SERVER[ "HTTP_" + STRTRAN( UPPER( aVal[ 1 ] ), "-", "_")] := AllTrim( aVal[ 2 ] ) + _SERVER[ "HTTP_" + STRTRAN( UPPER( LEFT( aRequest[nI], nJ - 1 ) ), "-", "_" ) ] := cI EXIT CASE "CONTENT-TYPE" CASE "CONTENT-LENGTH" @@ -1271,6 +1275,21 @@ STATIC FUNCTION ParseRequest( cRequest ) ENDIF NEXT + // check if Host field is provided + IF hb_HPos( _HTTP_REQUEST, "Host" ) == 0 + + // Try to determine Host name + IF !Empty( hUrl[ "HOST" ] ) + _HTTP_REQUEST[ "Host" ] := hUrl[ "HOST" ] + ELSE + _HTTP_REQUEST[ "Host" ] := "" + // Set status code + t_nStatusCode := 400 // Bad Request + RETURN .F. + ENDIF + + ENDIF + //hb_toOutDebug( "_HTTP_REQUEST: aRequest = %s, _HTTP_REQUEST = %s\n\r", hb_ValToExp( aRequest ), hb_ValToExp( _HTTP_REQUEST ) ) // GET @@ -1306,18 +1325,18 @@ STATIC FUNCTION ParseRequest( cRequest ) //hb_toOutDebug( "COOKIE: cFields = %s, hVars = %s, _COOKIE = %s, _REQUEST = %s\n\r", cFields, hb_ValToExp( hVars ), hb_ValToExp( _COOKIE ), hb_ValToExp( _REQUEST ) ) + // define _HTTP_RESPONSE + _HTTP_RESPONSE[ "X-Powered-By" ] := Version() + _HTTP_RESPONSE[ "Connection" ] := "Close" + _HTTP_RESPONSE[ "Content-Type" ] := "text/html; charset=UTF-8" + _HTTP_RESPONSE[ "Server" ] := APP_NAME + " " + APP_VERSION + //_HTTP_RESPONSE[ "Transfer-Encoding" ] := "chunked" + // Complete _SERVER _SERVER[ "SERVER_NAME" ] := uhttpd_split( ":", _HTTP_REQUEST[ "HOST" ], 1 )[ 1 ] - _SERVER[ "SERVER_SOFTWARE" ] := APP_NAME + " " + APP_VERSION + " (" + OS() + ")" - _SERVER[ "SERVER_SIGNATURE" ] := "
" + _SERVER[ "SERVER_SOFTWARE" ] + " Server at " + _SERVER[ "SERVER_NAME" ] + " Port " + _SERVER[ "SERVER_PORT" ] + "
" - _SERVER[ "DOCUMENT_ROOT" ] := s_cDocumentRoot - _SERVER[ "SERVER_ADMIN" ] := "root@localhost" // TOFIX: put real user _SERVER[ "SCRIPT_FILENAME" ] := STRTRAN( STRTRAN( _SERVER[ "DOCUMENT_ROOT" ] + _SERVER[ "SCRIPT_NAME" ], "//", "/" ), "\", "/" ) - _SERVER[ "GATEWAY_INTERFACE" ] := "CGI/1.1" - _SERVER[ "SCRIPT_URL" ] := _SERVER["SCRIPT_NAME"] - _SERVER[ "SCRIPT_URI" ] := "http://" + _HTTP_REQUEST[ "HOST" ] + _SERVER["SCRIPT_NAME"] - _SERVER[ "PATH_INFO" ] := NIL - _SERVER[ "PATH_TRANSLATED" ] := NIL + _SERVER[ "SCRIPT_URL" ] := _SERVER[ "SCRIPT_NAME" ] + _SERVER[ "SCRIPT_URI" ] := "http://" + _HTTP_REQUEST[ "HOST" ] + _SERVER[ "SCRIPT_NAME" ] //hb_ToOutDebug( "_SERVER = %s\n\r", hb_ValToExp( _SERVER ) ) //hb_ToOutDebug( "_GET = %s\n\r", hb_ValToExp( _GET ) ) @@ -1325,26 +1344,28 @@ STATIC FUNCTION ParseRequest( cRequest ) //hb_ToOutDebug( "_COOKIE = %s\n\r", hb_ValToExp( _COOKIE ) ) //hb_ToOutDebug( "_SESSION = %s\n\r", hb_ValToExp( _SESSION ) ) //hb_ToOutDebug( "_HTTP_REQUEST = %s\n\r", hb_ValToExp( _HTTP_REQUEST ) ) + //hb_ToOutDebug( "_HTTP_RESPONSE = %s\n\r", hb_ValToExp( _HTTP_RESPONSE ) ) // After defined all SERVER vars we can define a session // SESSION - sessions ID is stored as a cookie value, normally as SESSIONID var name (this can be user defined) - t_oSession := uhttpd_SessionNew( "SESSION", s_cSessionPath ) + t_oSession := uhttpd_SessionNew( "UHTTPD-SESSION", s_cSessionPath ) t_oSession:Start() - RETURN .T. STATIC FUNCTION MakeResponse() - LOCAL cRet, cReturnCode + LOCAL cRet, cReturnCode, v - uhttpd_AddHeader( "Connection", "close" ) + //uhttpd_SetHeader( "X-Powered-By", Version() ) + + //uhttpd_SetHeader( "Connection", "close" ) IF uhttpd_GetHeader( "Location" ) != NIL t_nStatusCode := 301 ENDIF IF uhttpd_GetHeader( "Content-Type" ) == NIL - uhttpd_AddHeader( "Content-Type", "text/html" ) + uhttpd_SetHeader( "Content-Type", "text/html" ) ENDIF cRet := "HTTP/1.1 " @@ -1360,13 +1381,19 @@ STATIC FUNCTION MakeResponse() CASE 402 CASE 403 CASE 404 + CASE 405 + CASE 500 + CASE 501 + CASE 502 CASE 503 + CASE 504 + CASE 505 t_cResult := "

" + cReturnCode + "

" EXIT // extended error messages - from Microsoft IIS Server CASE 500013 // error: 500-13 Server too busy - uhttpd_AddHeader( "Retry-After", "60" ) // retry after 60 seconds + uhttpd_SetHeader( "Retry-After", "60" ) // retry after 60 seconds t_cResult := "

500 Server Too Busy

" EXIT @@ -1380,14 +1407,25 @@ STATIC FUNCTION MakeResponse() //hb_ToOutDebug( "_SESSION = %s\n\r", hb_ValToExp( _SESSION ) ) // Close session - Autodestructor will NOT close it, because t_oSession is destroyed only at end of Thread - t_oSession:Close() + + IF HB_ISOBJECT( t_oSession ) + t_oSession:Close() + ENDIF // t_oSession := NIL WriteToConsole( cReturnCode ) cRet += cReturnCode + CR_LF - AEVAL( t_aHeader, {|x| cRet += x[1] + ": " + x[2] + CR_LF } ) + + FOR EACH v IN _HTTP_RESPONSE + cRet += v:__enumKey() + ": " + v:__enumValue() + CR_LF + NEXT + + //AEVAL( t_aHeader, {|x| cRet += x[1] + ": " + x[2] + CR_LF } ) cRet += CR_LF cRet += t_cResult + + //hb_ToOutDebug( "_HTTP_RESPONSE = %s\n\rcRet = %s\n\r", hb_ValToExp( _HTTP_RESPONSE ), cRet ) + RETURN cRet STATIC FUNCTION DecodeStatusCode() @@ -1415,9 +1453,27 @@ STATIC FUNCTION DecodeStatusCode() CASE 404 cReturnCode := "404 Not Found" EXIT + CASE 405 + cReturnCode := "405 Method Not Allowed" + EXIT + CASE 500 + cReturnCode := "500 Internal Server Error" + EXIT + CASE 501 + cReturnCode := "501 Not Implemented" + EXIT + CASE 502 + cReturnCode := "502 Bad Gateway" + EXIT CASE 503 cReturnCode := "503 Service Unavailable" EXIT + CASE 504 + cReturnCode := "504 Gateway Timeout" + EXIT + CASE 505 + cReturnCode := "505 HTTP Version Not Supported" + EXIT // extended error messages - from Microsoft IIS Server CASE 500013 // error: 500-13 Server too busy @@ -1676,31 +1732,49 @@ PROCEDURE uhttpd_SetStatusCode(nStatusCode) RETURN -PROCEDURE uhttpd_AddHeader( cType, cValue, lReplace ) - LOCAL nI - DEFAULT lReplace TO TRUE // Needed from SetCookie() +PROCEDURE uhttpd_SetHeader( cType, cValue ) + //LOCAL nI + //DEFAULT lReplace TO TRUE // Needed from SetCookie() + hb_HSet( _HTTP_RESPONSE, cType, cValue ) + + /* IF lReplace .AND. ( nI := ASCAN( t_aHeader, {|x| UPPER( x[ 1 ] ) == UPPER( cType ) } ) ) > 0 t_aHeader[ nI, 2 ] := cValue ELSE AADD( t_aHeader, { cType, cValue } ) ENDIF + */ + RETURN -FUNCTION uhttpd_GetHeader( cType, /*@*/ nPos ) + +FUNCTION uhttpd_GetHeader( cType ) + RETURN uhttpd_HGetValue( _HTTP_RESPONSE, cType ) +/* DEFAULT nPos TO 1 + + nPos := hb_HPos( hHash, cKey )) IF ( nPos := ASCAN( t_aHeader, {|x| UPPER( x[ 1 ] ) == UPPER( cType ) }, nPos ) ) > 0 RETURN t_aHeader[ nPos, 2 ] ENDIF RETURN NIL +*/ PROCEDURE uhttpd_DelHeader( cType ) + LOCAL nPos := hb_HPos( _HTTP_RESPONSE, cType ) + IF nPos > 0 + hb_HDel( _HTTP_RESPONSE, nPos ) + ENDIF + RETURN +/* LOCAL nI IF ( nI := ASCAN( t_aHeader, {|x| UPPER( x[ 1 ] ) == UPPER( cType ) } ) ) > 0 hb_aDel( t_aHeader, nI, TRUE ) ENDIF RETURN +*/ PROCEDURE uhttpd_Write( cString ) t_cResult += cString @@ -1787,10 +1861,12 @@ STATIC FUNCTION sendReply( hSocket, cSend ) RETURN nError -STATIC PROCEDURE defineServerAdresses( hSocket ) +STATIC PROCEDURE defineServer( hSocket ) #ifndef USE_HB_INET LOCAL aI #endif + + // define _SERVER vars (address part) #ifdef USE_HB_INET _SERVER[ "REMOTE_ADDR" ] := hb_InetAddress( hSocket ) _SERVER[ "REMOTE_HOST" ] := _SERVER[ "REMOTE_ADDR" ] // no reverse DNS @@ -1811,6 +1887,35 @@ STATIC PROCEDURE defineServerAdresses( hSocket ) ENDIF #endif + // add other _SERVER vars + _SERVER[ "REQUEST_METHOD" ] := NIL + _SERVER[ "REQUEST_URI" ] := NIL + _SERVER[ "SERVER_PROTOCOL" ] := NIL + _SERVER[ "SCRIPT_NAME" ] := NIL + _SERVER[ "QUERY_STRING" ] := NIL + _SERVER[ "HTTP_ACCEPT" ] := NIL + _SERVER[ "HTTP_ACCEPT_CHARSET" ] := NIL + _SERVER[ "HTTP_ACCEPT_ENCODING" ] := NIL + _SERVER[ "HTTP_ACCEPT_LANGUAGE" ] := NIL + _SERVER[ "HTTP_CONNECTION" ] := NIL + _SERVER[ "HTTP_HOST" ] := NIL + _SERVER[ "HTTP_KEEP_ALIVE" ] := NIL + _SERVER[ "HTTP_REFERER" ] := "" + _SERVER[ "HTTP_USER_AGENT" ] := "" + _SERVER[ "HTTP_CACHE_CONTROL" ] := NIL + _SERVER[ "HTTP_COOKIE" ] := NIL + _SERVER[ "SERVER_NAME" ] := "" + _SERVER[ "SERVER_SOFTWARE" ] := APP_NAME + " " + APP_VERSION + " (" + OS() + ")" + _SERVER[ "SERVER_SIGNATURE" ] := "
" + _SERVER[ "SERVER_SOFTWARE" ] + " Server at " + _SERVER[ "SERVER_NAME" ] + " Port " + _SERVER[ "SERVER_PORT" ] + "
" + _SERVER[ "DOCUMENT_ROOT" ] := s_cDocumentRoot + _SERVER[ "SERVER_ADMIN" ] := "root@localhost" // TOFIX: put real user + _SERVER[ "SCRIPT_FILENAME" ] := NIL + _SERVER[ "GATEWAY_INTERFACE" ] := "CGI/1.1" + _SERVER[ "SCRIPT_URL" ] := NIL + _SERVER[ "SCRIPT_URI" ] := NIL + _SERVER[ "PATH_INFO" ] := NIL + _SERVER[ "PATH_TRANSLATED" ] := NIL + RETURN FUNCTION uhttpd_split( cSeparator, cString, nMax ) @@ -1913,7 +2018,7 @@ STATIC FUNCTION uproc_default() // if it exists as folder and it is missing trailing slash I add it and redirect to it IF RIGHT( cFileName, 1 ) != "/" - uhttpd_AddHeader( "Location", "http://" + _SERVER[ "HTTP_HOST" ] + _SERVER[ "SCRIPT_NAME" ] + "/" ) + uhttpd_SetHeader( "Location", "http://" + _SERVER[ "HTTP_HOST" ] + _SERVER[ "SCRIPT_NAME" ] + "/" ) RETURN MakeResponse() ENDIF @@ -2020,7 +2125,7 @@ STATIC PROCEDURE Define_Env( hmServer ) STATIC PROCEDURE ShowServerStatus() LOCAL cThreads - uhttpd_AddHeader( "Content-Type", "text/html" ) + uhttpd_SetHeader( "Content-Type", "text/html" ) uhttpd_Write( '' ) uhttpd_Write( '' ) uhttpd_Write( '' ) @@ -2063,7 +2168,7 @@ STATIC PROCEDURE ShowFolder( cDir ) LOCAL aDir, aF LOCAL cParentDir, nPos - uhttpd_AddHeader( "Content-Type", "text/html" ) + uhttpd_SetHeader( "Content-Type", "text/html" ) aDir := DIRECTORY( uhttpd_OSFileName( cDir ), "D" ) IF HB_HHasKey( _GET, "s" ) @@ -2607,7 +2712,7 @@ STATIC FUNCTION Handler_Default( cFileName ) cMime := "application/octet-stream" ENDIF - uhttpd_AddHeader( "Content-Type", cMime ) + uhttpd_SetHeader( "Content-Type", cMime ) uhttpd_Write( HB_MEMOREAD( uhttpd_OSFileName( cFileName ) ) ) // Directory content request @@ -2635,7 +2740,7 @@ RETURN MakeResponse() // This handler handle server status STATIC FUNCTION Handler_ServerStatus() LOCAL cThreads - uhttpd_AddHeader( "Content-Type", "text/html" ) + uhttpd_SetHeader( "Content-Type", "text/html" ) uhttpd_Write( '' ) uhttpd_Write( '' ) uhttpd_Write( '' ) @@ -2715,7 +2820,7 @@ STATIC FUNCTION Handler_HrbScript( cFileName ) ENDIF IF HB_ISSTRING( xResult ) - uhttpd_AddHeader( "Content-Type", "text/html" ) + uhttpd_SetHeader( "Content-Type", "text/html" ) uhttpd_Write( xResult ) ELSE // Application in HRB module is responsible to send HTML content @@ -2725,7 +2830,7 @@ STATIC FUNCTION Handler_HrbScript( cFileName ) WriteToConsole( "Error!" ) - uhttpd_AddHeader( "Content-Type", "text/html" ) + uhttpd_SetHeader( "Content-Type", "text/html" ) uhttpd_Write( "Error" ) uhttpd_Write( "
Description: " + hb_cStr( oError:Description ) ) uhttpd_Write( "
Filename: " + hb_cStr( oError:filename ) ) @@ -2748,13 +2853,13 @@ STATIC FUNCTION Handler_CgiScript( cFileName ) IF ( CGIExec( uhttpd_OSFileName(cFileName), @xResult ) ) == 0 - //uhttpd_AddHeader( "Content-Type", cI ) + //uhttpd_SetHeader( "Content-Type", cI ) //uhttpd_Write( xResult ) RETURN "HTTP/1.1 200 OK " + CR_LF + xResult ELSE - uhttpd_AddHeader( "Content-Type", "text/html" ) + uhttpd_SetHeader( "Content-Type", "text/html" ) IF !Empty( xResult ) uhttpd_Write( xResult ) ELSE