Socket Negativity

GregTomkins

Active Member
10.2B05, HP-UX

I have code that uses ABL sockets to communicate with a web server. It's been running for weeks without incident. Today, we installed it on a different server. Suddenly, it fails 100% of the time (on that server only).

The symptom is that following a seemingly normal connection, GET-BYTES-AVAILABLE (which normally returns sensible positive values) consistently returns '-276921288'.

Progress KB has nothing of value that I could find. The Internet says that negative values in TCP reply lengths are associated with a reply that isn't properly delimited. But I am pretty sure (I don't have access to it) the remote host is responding correctly since it correctly responds to requests from many other servers.

Same OS and Progress version in all cases
No external configuration (that I can see) that could vary between servers
I cannot deduce any significance to the value 276921288
I don't have access to curl, WireShark, tcpdump, nettl etc. on this area of our network

The only difference (that I can think of) that I can't be 100% sure about is the surrounding network environment as it is managed by people that I have no access to. Anyway, if there was a proxy screwing things up, or similar, I would expect it to outright refuse the connection.

Any ideas?
 

Cecil

19+ years progress programming and still learning.
Working with memptrs can be bit of a pig. I find that memptr don't automatically have a default size of zero. Just because you have a variable definition of a memptr don't assume it will be zero.

If you expect it to have a size of zero set it manually anyway. set-size(memptr) = 0.

I learnt this the hard way myself many years ago on a production system. Worked okay in development for the short amount of time, but once it was being extensively used I then had a similar problem to yourself.
 

Cecil

19+ years progress programming and still learning.
Extra Thoughts:
Is -276,921,288 a integer overflow?

Since socket programming relies on the OS, could there be something to do with the TCP buffer/packet size?

Are to downloading a compressed stream and waiting for a particular EOF marker? Cont: is this a small or large data you are receiving..i.e Plain text of Video/PDFs? (Understand if you don't want to give to much away).
 

TomBascom

Curmudgeon
Does your read handler set:

self:sensitive = no.

as it's very first action? And then set:

self:sensitive = yes.

Just prior to returning?

Are you setting anything like:

httpSocket:set-socket-option ( "SO-RCVBUF", string( 64 * 1024 * 1024 )). /** allow 64MB */

when you open the socket?
 

GregTomkins

Active Member
Thanks a lot for your suggestions. Still the same problem though.

Here's the most recent code, stripped down to its essentials. Our networking guys claim everything is OK because one can Telnet into port 443 on xxx without a problem. The only difference I can see is that the servers are located in different cities with different networking around them, but since Telnet works, it seems that shouldn't matter.

This code works 100% reliably on every server except one. Everywhere else, the reply length is 144 bytes - if you actually read it, it's a blob of JSON informing that none of the data that it expects is present.

On the 'bad server', if you ignore the bizarre value of GET-BYTES-AVAILABLE and try to read the socket anyway, Progress claims that the socket is unusable.

Code:
DEF VAR s AS HANDLE.
CREATE SOCKET s.

s:CONNECT("-H xxx -S 443 -ssl -nohostverify") NO-ERROR.
MESSAGE ERROR-STATUS:GET-MESSAGE(1) s:CONNECTED().

s:set-socket-option ( "SO-RCVBUF", string( 64 * 1024 * 1024 )). /** allow 64MB */

s:SET-READ-RESPONSE-PROCEDURE("int_read_handler":U).
RUN int_do_post(s, "xxx", "/v1/libraryItems").

s:DISCONNECT().  

PROCEDURE int_do_post:
    DEF INPUT  PARAM p_socket        AS HANDLE NO-UNDO.
    DEF INPUT  PARAM p_base_url      AS CHAR   NO-UNDO.
    DEF INPUT  PARAM p_operation     AS CHAR   NO-UNDO.
    DEF VAR h_outbuf  AS MEMPTR NO-UNDO.
    DEF VAR h_request AS CHAR   NO-UNDO.
    h_request = "":U.
    h_request = h_request + "POST ":U + p_operation + " HTTP/1.1":U + "~n":U.
    h_request = h_request + "~n":U.
    h_request = h_request + "~n":U.
    SET-SIZE(h_outbuf) = LENGTH(h_request) + 1.
    PUT-STRING(h_outbuf, 1) = h_request.
    p_socket:WRITE(h_outbuf, 1, LENGTH(h_request)).
    SET-SIZE(h_outbuf) = 0.
    DO WHILE TRUE:
        WAIT-FOR READ-RESPONSE OF p_socket PAUSE 1.
    END.
END.

PROCEDURE int_read_handler:
    SELF:SENSITIVE = FALSE.
    MESSAGE s:GET-BYTES-AVAILABLE(). /* 144 or -276921448 */
    SELF:SENSITIVE = TRUE.
END.
 

TomBascom

Curmudgeon
I'll bet they have screwed up the SSL somehow. I think you're going to have to get access to "curl" or the ilk to prove it.
 

Cecil

19+ years progress programming and still learning.
Correct me if I'm wrong, but does'nt each line of the HTTP header need to be terminated by a
CRLF "~r~n" rather than just a LF "~n"??

Also is byte-order a consideration??
SET-BYTE-ORDER(h_outbuf) = BIG-ENDIAN.
 

TomBascom

Curmudgeon
The header line terminator should be chr(10) + chr(13) -- I believe that "~r~n" will result in LF+LF+CR on Windows.
 

TomBascom

Curmudgeon
I'd like to submit that post as clear and indisputable evidence that I had not finished my coffee by 8:12 this morning.
 

Cecil

19+ years progress programming and still learning.
Outputting "~n" writes CR LF to the file on Windows.

I did the the following code on Windows and it only output the expected 'Line Feed' character (0x0a).

Code:
define variable rawData    as memptr    no-undo.
define variable stringData as character no-undo.

assign
    stringData = "~n".

set-size(rawData) = 0. /* Flush the buffer to zero bytes. */
set-size(rawData) = 2. /* Allow 2 bytes for this experiment. */

put-string(rawData,1) = stringData . /* add a New Line starting at the first position of the memprt. */

/** export the memptr to file on disk. **/
copy-lob rawData to file 'CRLF_Test_1.txt'.

set-size(rawData) = 0. /* House clearing, flush the buffer again..*/

ScreenShot185.png

However if you output a stream to disk, then yes '~n' does get translated to a CRLF (0x0d,0x0a).

Code:
define stream outputStream.

output stream outputStream to 'CRLF_Test_2.txt'.

put stream outputStream "~n".

output stream outputStream close.

ScreenShot186.png

Conclusion, using "~n" does not behave the same way all the time and since we are talking about socket programing using memptrs to send and receive data, I believe that '~n' is not being interpreted into a CRLF on Windows or any other OS.

Side note:
Sorry if I'm teaching you guys to suck eggs, but if you did actually wanted to write to a file of a LF exclusively that's where you used the 'control' option on the 'put' statement.

Code:
put stream outputStream control chr(0x0a).
 

Cecil

19+ years progress programming and still learning.
Refactored the code to use CRLF to be non OS depentant:

Code:
DEF VAR s AS HANDLE.

&SCOPED-DEFINE CRLF chr(0x0d) + chr(0x0a)

CREATE SOCKET s.
s:CONNECT("-H xxx -S 443 -ssl -nohostverify") NO-ERROR.
MESSAGE ERROR-STATUS:GET-MESSAGE(1) s:CONNECTED().

s:set-socket-option ( "SO-RCVBUF", string( 64 * 1024 * 1024 )). /** allow 64MB */
s:SET-READ-RESPONSE-PROCEDURE("int_read_handler":U).
RUN int_do_post(s, "xxx", "/v1/libraryItems").
s:DISCONNECT(). 
PROCEDURE int_do_post:
    DEF INPUT  PARAM p_socket        AS HANDLE NO-UNDO.
    DEF INPUT  PARAM p_base_url      AS CHAR   NO-UNDO.
    DEF INPUT  PARAM p_operation     AS CHAR   NO-UNDO.
    DEF VAR h_outbuf  AS MEMPTR NO-UNDO.
    DEF VAR h_request AS CHAR   NO-UNDO.
    h_request = "":U.
    h_request = h_request + "POST ":U + p_operation + " HTTP/1.1":U + {&CRLF}.
    h_request = h_request + {&CRLF}.
    h_request = h_request + {&CRLF}.
    SET-SIZE(h_outbuf) = LENGTH(h_request) + 1.
    PUT-STRING(h_outbuf, 1) = h_request.
    p_socket:WRITE(h_outbuf, 1, LENGTH(h_request)).
    SET-SIZE(h_outbuf) = 0.
    DO WHILE TRUE:
        WAIT-FOR READ-RESPONSE OF p_socket PAUSE 1.
    END.
END.
PROCEDURE int_read_handler:
    SELF:SENSITIVE = FALSE.
    MESSAGE s:GET-BYTES-AVAILABLE(). /* 144 or -276921448 */
    SELF:SENSITIVE = TRUE.
END.
 

GregTomkins

Active Member
Thank you for your comments.

We use Unix exclusively and have used the '~n' in production since forever, so I assume it works and in any case that should cause a HTTP problem, not a socket problem. Also, since this works on one server and not the other, despite their having identical ABL code, OS's and Progress versions, I don't think that's the issue.

I found a vaguely similar problem on another server caused by different code pages. You'd think (well, *I*'d think) a code page screw up would garble data, but not actually crash PSC's TCP implementation (and the AppServer agent), but it seems it can do exactly that. However, I can't correlate that to the problem in this thread. Right now I'm holding onto a faint hope that PSC Tech Support will come through

Meanwhile I'm making plans to deploy this in Java, which I know for sure works. I don't want to do that because it will confuse things for us, but I suspect it's the only way.
 

Rob Fitzpatrick

ProgressTalk.com Sponsor
Is it possible that a buggy/out-of-date NIC driver or firmware could cause the OS to report a bad "bytes available" amount to OpenEdge?
 

Cecil

19+ years progress programming and still learning.
I so curious to know what is wrong with your problem. Keep us updated with progress in getting this fixed. Hopefully you don't have to use Java as a workaround solution.
 
Top