6

Apparently un-catchable error while toying around with Rebol/Core (278-3-1) to make a kind-of web-server to serve a static text, containing a redirect link to a new service location.

The specific location of the error appear to be in example code written by Carl Sassenrath himself, back in 2006, so I'm kind of baffled there could be an undetected error after all these years.

I have three of these scripts running simultaneous, monitoring three individual ports. Essentially the script works as it should... when accessed repeatedly with multiple browsers at once (on all parallel scripts) it appear appear to be pretty stable... but one after another they fail. Sometimes after 2 minutes, sometimes after 20 minutes - after adding the print statements sometimes even after 60 minutes - but eventually they will fail like this:

** Script Error: Out of range or past end
** Where: forever
** Near: not empty? request: first http-port

I've tried wrapping just about every part of the program loop in a try[][exception], but the error still occurs. Unfortunately my search-fu appear to be weak this time of year, as I haven't found anything that could explain the problem.

The code is a cut down version of Carl Sassenrath's Tiny Web Server, slightly modified to bind to a specific IP, and to emit HTML instead of loading files:

REBOL [title: "TestMovedServer"]
AppName: "Test"
NewSite: "http://test.myserver.org"

listen-port: open/lines tcp://:81   browse http://10.100.44.6?
buffer: make string! 1024  ; will auto-expand if needed

forever [
    http-port: first wait listen-port
    clear buffer

    while [not empty? request: first http-port][
        print request
        repend buffer [request newline]
        print "----------"
    ]
    repend buffer ["Address: " http-port/host newline] 
    print buffer
    Location: ""
    mime: "text/html"
    parse buffer ["get" ["http" | "/ " | copy Location to " "]]

    data: rejoin [{
        <HTML><HEAD><TITLE>Site Relocated</TITLE></HEAD>
        <BODY><CENTER><BR><BR><BR><BR><BR><BR>
        <H1>} AppName { have moved to <A HREF="} NewSite {">} NewSite {</A></H1>
        <BR><BR><BR>Please update the link you came from.
        <BR><BR><BR><BR><BR><A HREF="} NewSite Location {">(Continue directly to the requested page)</A>
        </CENTER></BODY></HTML>
    }]  
    insert data rejoin ["HTTP/1.0 200 OK^/Content-type: " mime "^/^/"]
    write-io http-port data length? data
    close http-port
    print "============"
]

I'm looking forward to see what you guys make out of this!

fsteff
  • 543
  • 5
  • 19

2 Answers2

3

You get an error when trying to read from a closed connection. This seems to work.

n: 0
forever [
   http-port: first wait listen-port
   clear buffer
   if attempt [all [request: first http-port  not empty? request]] [
      until [
        print request
        repend buffer [request newline]
        print "----------"
        any [not request: first http-port empty? request]
      ]
      repend buffer ["Address: " http-port/host newline] 
      print buffer
      Location: ""
      mime: "text/html"
      parse buffer ["get" ["http" | "/ " | copy Location to " "]]

      data: rejoin [{
        <HTML><HEAD><TITLE>Site Relocated</TITLE></HEAD>
        <BODY><CENTER><BR><BR><BR><BR><BR><BR>
        <H1>} AppName n: n + 1 { has moved to <A HREF="} NewSite {">} NewSite {</A></H1>
        <BR><BR><BR>Please update the link you came from.
        <BR><BR><BR><BR><BR><A HREF="} NewSite Location {">(Continue directly to the requested page)</A>
        </CENTER></BODY></HTML>
      }]  
      insert data rejoin ["HTTP/1.0 200 OK^/Content-type: " mime "^/^/"]
      write-io http-port data length? data
  ]
  attempt [close http-port]
  print "============"
]
sqlab
  • 6,412
  • 1
  • 14
  • 29
  • Care to explain how you reached the conclusion that the original code was reading from a closed connection? Debugging steps would be appreciated. – fsteff Dec 22 '15 at 13:53
  • The conclusion comes from a mixture of consideration and trial and a big amount of experience – sqlab Dec 24 '15 at 09:30
1

Let us see the documentation for empty? Summary:

Returns TRUE if a series is at its tail. Usage:

empty? series Arguments:

series - The series argument. (must be: series port bitset)

So empty? requires series, port or bitset or string argument. Your variable (request) is getting any of them as long as there is connection to the port is open. empty? can thereafter determine whether it is at the tail of variable. When the connection is closed/interrupted, your variable receives nothing but there is access error connecting to port. Error does not have tail. empty? gets confused and crashes with error.

sqlab has replaced empty? with attempt

if attempt [all [request: first http-port  not empty? request]]

The ATTEMPT function is a shortcut for the frequent case of:

error? try [block]

with all he is guarding against error as well as none. ATTEMPT returns the result of the block if an error did not occur. If an error did occur, a NONE is returned. also with until and

any [not request: first http-port empty? request]

he is guarding against both.

Therefore his code is working.

Satish
  • 133
  • 8
  • Thank you for an excellent and very educational answer. Exactly the reasoning I was hoping for. – fsteff Jan 17 '16 at 17:17