On Thu, 30 Jul 2009, Przemyslaw Czerpak wrote:

Hi,

> BTW why did you use different stop condtions in current readRequest()
>     code instead of exact replication of above socket_*() code, i.e:
> 
>       cRequest := ""
>       nLen     := 1
>       cBuf     := Space( 4096 )
>       DO WHILE AT( CR_LF + CR_LF, cRequest ) == 0 .AND. nLen > 0
>          nLen := hb_InetRecv( hSocket, @cBuf )
>          cRequest += Left( cBuf, nLen )
>       ENDDO
>    ???

I looked closer at this wole code to find the answer myslef and as I can
see the version which uses socket_*() API is wrong. It can work but does
not have to.

   cRequest := ""
   nLen     := 1
   DO WHILE AT( CR_LF + CR_LF, cRequest ) == 0 .AND. nLen > 0
      nLen := socket_recv( hSocket, @cBuf )
      cRequest += cBuf
   ENDDO

In above code you do not check for additional CONTENT-LENGTH data.
If you are lucky man and your sever IP buffer receive in one IP packet
terminating CR_LF + CR_LF with the CONTENT-LENGTH data then it will
be attached to cBuf read in last socket_recv(). If you are not so lucky,
i.e. IP connection divides data into smaller peaces or data exceed
single IP size and your code was too fast and read data before kernel
joined IP frames so they can be accessed by single recv() then it will
not work correctly. It means that this code has random behavior in all
low level socket libraries and have to be fixed.
It's not guarantied that single recv() returns more then 1 character and
there is no guarantied that you receive in single recv() the same number
of bytes as you send in single send(). The data can be routed  through
different networks with different transport layers so it can be divided
into very small peaces and then joined by receivers. It's TCP/IP feature.
You should write all code expecting the worst possible case, i.e. single
byte from each recv() or joined many send() buffers. Number of bytes
read by single recv() is out of programmer control and it depends on
random for programmer conditions so even extensive tests cannot confirm
that code is correct if it's not written to be safe for any number of
bytes read by single recv().
I think you should look at Mindaugas' uhttpd2 code and function
ParseRequestHeader() and take the algorithm from it.

Below is fixed readRequest() from httpsrv but I think you should use
more elegant solution in like in Mindaugas code for final version.
Use this as temporary fix.
I haven't check rest of httpsrv code but if you have any other places
where you expect some strictly defined size of single socket_recv()
or hb_inetRecv() then you must fix them.
It may also explains some random behavior in your tests or why sometimes
it was not working correctly, i.e. once per few times the CONTENT-LENGTH
data was dropped or cut when socket_*() API were used.

HTH.

best regards,
Przemek



STATIC FUNCTION readRequest( hSocket, /* @ */ cRequest )
   LOCAL cBuf, nLen, nPos

   /* receive query */
#ifdef USE_HB_INET
   cRequest := hb_InetRecvEndBlock( hSocket, CR_LF + CR_LF, @nLen )
#else
   cRequest := ""
   DO WHILE .T.
      nLen := socket_recv( hSocket, @cBuf )
      IF nLen <= 0
         EXIT
      ENDIF
      cRequest += cBuf
      IF CR_LF + CR_LF $ cRequest
         EXIT
      ENDIF
   ENDDO
#endif

   /* receive CONTENT-LENGTH data */
   IF nLen > 0
      nPos := HB_ATI( CR_LF + "CONTENT-LENGTH:", cRequest )
      IF nPos > 0
         nPos := Val( Substr( cRequest, nPos + 17, 10 ) )
         IF nPos > 0
#ifdef USE_HB_INET
            cBuf := Space( nPos )
            nLen := hb_InetRecvAll( hSocket, @cBuf, nPos )
            IF nLen < 0
               nLen := -1
            ELSE
               cRequest += Left( cBuf, nLen )
            ENDIF
#else
            /* we have to decrease number of bytes to read by already read
             * data after CR_LF + CR_LF
             */
            nPos -= Len( cRequest ) - At( CR_LF + CR_LF, cRequest ) - 3
            WHILE nPos > 0
               nLen := socket_recv( hSocket, @cBuf, nPos )
               IF nLen <= 0
                  EXIT
               ENDIF
               cRequest += cBuf
               nPos -= nLen
            ENDDO
#endif
         ENDIF
      ENDIF
   ENDIF

#ifdef DEBUG_ACTIVE
   hb_ToOutDebug( "readRequest(): nLen = %i, cRequest = %s \n\r", nLen, 
cRequest )
#endif

   RETURN nLen
_______________________________________________
Harbour mailing list
Harbour@harbour-project.org
http://lists.harbour-project.org/mailman/listinfo/harbour

Reply via email to