Shop OBEX P1 Docs P2 Docs Learn Events
[but worked around] Not all images on page getting loaded. — Parallax Forums

[but worked around] Not all images on page getting loaded.

Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
edited 2011-03-07 00:50 in Accessories
'Just wondering if my Spinneret server software is the victim of a timeout mechanism I don't understand and how I might go about fixing it. My webcam page includes links to six dynamically-generated images. They don't alway all get loaded by my browser, though. Here's an example:

attachment.php?attachmentid=78918&d=1299195947

Here's the HTML source with the links to the missing images highlighted:
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
    <title>SpinneretCAM</title>
  </head>
  <body style="background-color: #ccccaa">
    <div align="center" style="color:navy;font-size:20pt;font-family:sans-serif">
      <hr/>
      <b>Spinneret WebCam</b><hr/>
      <div style="color:black;font-size:10pt">
        <form method="get" action="/change">
          <img src=/webcam.bmp?pan=5&target=6.00&view=21&map=Gray width=384 height=288/><p/>
          Sensor exposure is set dynamically, depending on the light level. The<br/>
          goal is to keep the average pixel as close to the user-settable target<br/>
          value as possible by adjusting the exposure time and gain setting.<p/>
          <table style="font-size:10pt">
            <tr align="center">
              <td>
                Current exposure time: <b style="color:red">7.77</b> ms.<br/>
                 &nbsp;0 <img src=/hbar.bmp?v=59&fg=ff0000/> 21<br/>
                 &nbsp;0 [color=red]<img src=/hbar.bmp?v=77&fg=00aa00/>[/color] 31<br/>
                Current gain setting: <b style="color:#00aa00">15</b><br/>
              </td>
              <td>
                Target average brightness: <b style="color:#6666ff">6.00</b><br/>
                 &nbsp;0 [color=red]<img src=/hbar.bmp?v=64&fg=6666ff/>[/color] 15<br/>
                 &nbsp;0 <img src=/hbar.bmp?v=58&fg=6666ff/> 15<br/>
                Actual average brightness: <b style="color:#6666ff">5.48</b><br/>
              </td>
            </tr>
          </table>
          <p/>
          <input type="submit" name="change" value="Cool" style="width:60px">
          <input type="submit" name="change" value="Gray" style="width:60px">
          <input type="submit" name="change" value="Warm" style="width:60px"> &nbsp;&nbsp;&nbsp;
          <input type="submit" name="change" value="Darker" style="width:60px">
          <input type="submit" name="change" value="Refresh" style="width:60px">
          <input type="submit" name="change" value="Lighter" style="width:60px"><p/>
          <table style="font-size:10pt">
            <tr align="center">
              <td>
                <input type="submit" name="change" value="|<<" style="width:30px">
              </td>
              <td>
                <input type="submit" name="change" value="<<" style="width:30px">
              </td>
              <td>
                <input type="submit" name="change" value="<" style="width:30px">
              </td>
              <td>
                Current pan position: <b style="color:#808080">5</b><br/>
                -5 <img src=/pan.bmp?5/> 5<br/>
              </td>
              <td>
                <input type="submit" name="change" value=">" style="width:30px">
              </td>
              <td>
                <input type="submit" name="change" value=">>" style="width:30px">
              </td>
              <td>
                <input type="submit" name="change" value=">>|" style="width:30px">
              </td>
            </tr>
          </table>
          <p/>
          <input type="hidden" name="target" value="6.00">
          <input type="hidden" name="pan" value="5">
          <input type="hidden" name="map" value="Gray"><p/>
          There have been <b>21</b> views since last reset.
        </form>
      </div>
    </div>
  </body>
</html>

The curious thing, as the attached "net sniff" dump shows, is that the browser never even requested the missing images. So I'm wondering:

1. Was there some kind of timeout in effect? IOW did the server fail to keep up with the browser's timing expectations and it just gave up requesting more stuff?

2. Did the browser want to request the images but got cut off when the Spinneret reset the socket between requests?

3. Or ... ?

One more note: This failure occurs on my Windows computer running Opera on the same local net. It has also been reported by a client from outside my local network. Curiously, I have yet to see this happen with Opera on my iMac (on the same local network), even though the images seem to take forever to load.

-Phil
469 x 636 - 54K
«1

Comments

  • Mike GMike G Posts: 2,702
    edited 2011-03-03 17:41
    Same thing happens to me. I think it's a bug in my logic that pops up under load or maybe something with the driver. My logs show various entries like a status 0x00 when I'm expecting a 0x14. I reset the socket and the image request is lost forever. Anyway, I think that's what's happening.

    I added caching. Caching helps a lot but it does not fix the problem. And it's not just images but any src file; css, javascript. It seems like there is an exponential relationship to the number of src files and time to download. I have no hard evidence and I'm reading files from the SD card.

    BTW, very nice WebCam page.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-04 10:15
    This is something that desperately needs to be solved. To test the reset theory, I commented out the highlighted reset in my get_request method:
    PUB get_request : packet_size | time
    
      repeat until w5100.SocketTCPestablished(0)
        ifnot (lookdown(get_status : $14, $16, $17, $11))
          [color=red]socket_reset[/color]
          return
      time := cnt
      repeat until (cnt - time > timeout)
        if (packet_size := W5100.rxTCP(0, @data))
          data[packet_size]~
          return
      socket_reset
      return
          
    PRI get_status : status
    
      w5100.readIND(w5100#_S0_SR, @status, 1)
    
    There was no change in behavior. Next I commented out the return that follows it. No change. So I don't think it has anything to do with that section of code. Also, my timeout is five seconds, and nothing is taking that long.

    That left me wondering if Opera was getting screwed up with caching, even though each image has the Cache-control: no-store pragma in its HTTP header. To test this, I added the views variable to the images' GET strings, so each address instance would be unique. 'Still no joy.

    -Phil
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-04 10:27
    'Turns out that Firefox is worse yet. It doesn't even load the webcam image, whereas Opera never fails to load it:

    attachment.php?attachmentid=78951&d=1299263105

    But I can right-click on any of the broken images, select Reload Image, and it displays right away.

    -Phil
    475 x 678 - 8K
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-04 11:13
    Some additional observations:

    1. Every browser appears to be trying to obtain the image data as soon as it encounters the <img> tag in the input stream, i.e. before the webpage containing it is completely loaded. At least that's how I interpret the SYNs from the client machine in the TCP dump.

    2. These SYNs are always answered by the Spinneret with a RST until it has sent the entire page.

    3. Some SYNs received by the Spinneret after the page has been sent are answered with SYN ACK; others, with RST. Those that get answered with SYN ACK are followed by requests for the image data, which the Spinneret then provides and which gets displayed.

    4. Opera on my iMac sends inter-page SYNs, as described above, but doesn't give up due to the RSTs and simply resends the image requests again later. But this process is ponderously slooooow. The PC is much snappier but seems to give up more quickly.

    It would appear that being able to signal the client not to try opening a new connection until it receives all data from a pending request would clear up this logjam. But I don't know how to do that or if it's even possible.

    -Phil
  • Mike GMike G Posts: 2,702
    edited 2011-03-04 12:09
    Nice work Phil!

    I agree this needs to be fixed. I just have not had the time to mess with it. I have a couple of PASM object I really want to finish up. Then I thought I would hammer on the WizNet driver, I guess really the WIZ5100.
    It would appear that being able to signal the client not to try opening a new connection until it receives all data from a pending request would clear up this logjam. But I don't know how to do that or if it's even possible.
    Phil, do you think this is a question for WIZ5100 support?
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-04 12:14
    Mike G wrote:
    Phil, do you think this is a question for WIZ5100 support?
    Mike, I just don't know. I was hoping to find an HTTP response header that I could employ to inhibit TCP multiplexing from the client end, but I've had no success so far.

    -Phil
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-04 12:52
    It turns out that this problem is solvable from the browser end -- at least with Opera -- by setting the "Max connections to a server" parameter to 1. This demonstrates, at least, that the problem does stem from TCP multiplexing and not from something else. But a client-side solution is not a real solution, since it puts the onus on the user to resolve the problem at his end -- and in a way that could detrimentally affect the browser's performance with other servers.

    -Phil
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-04 17:41
    I'm not going to call this SOLVED yet, but I've got a functioning workaround using Javascript. It involves changing all the <img src = x /> tags to <img title = x />. This prevents the images from being loaded, since they no longer have a src attribute. Next a loadimages method is defined that is executed once the page is loaded via an onload attribute for the <body> tag. The loadimages method then assigns the src attributes of all the document's images to their title attributes, waiting until each one has finished loading before going on to the next one. Here's an HTML snippet:
      <body onload=loadimages()  style="background-color: #ccccaa">
        <script language=javascript>
          function loadimages() {
            var i;
            var p=document.images;
            for(i=0;i<p.length;i++) {
              p[i].src = p[i].title;
              while(p[i].complete != true) {}
            }
          }
        </script>
        
        ...
        
        <img title=/webcam.bmp?pan=5&target=8.00&view=1&map=Warm width=384 height=288 />
    
    Once the page has loaded, you can actually see the images being loaded in the order of their appearance on the page. Although it works reliably, it seems like an ugly solution to a problem that should have a more elegant one.

    That, as it turns out, is with Opera only. Firefox is another matter. With Firefox, each image takes forever to finish loading; and, for each one, a box pops up after the image has loaded to say the script is stalled, do I want to continue? If I continue, it goes on to the next image, and so on, popping up the box for each until they're all loaded. I thought maybe it was simply timing out after each one because the images didn't have a Content-length: header. But adding that header didn't help.

    -Phil
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-05 00:34
    I turns out that Safari and Konqueror behave similarly to Firefox, and I think I know why. As long as JavaScript is in its while loop waiting for the load to complete, the browser is unable to update the DOM with the complete status. (Maybe it's even blocked from performing the image load.) I'm guessing that Opera is multi-threaded, which allows the browser to continue with its work while the JavaScript code is running. So I've either got to find a sleep function for unblocking the browser or else get out of loadimages and come up with a handler for image.onload.

    But that's for ma
  • kuismakuisma Posts: 134
    edited 2011-03-05 02:15
    Try using HTTP/1.1 and keep-alive. This way the client will reuse one and the same HTTP session for multiple requests. Alto, I'm not sure it will make the client use only one session, but I guess it'll mitigate your problem.
  • Mike GMike G Posts: 2,702
    edited 2011-03-05 07:11
    I set Connection: keep-alive. That did the trick. I thought that I already had the connect set to keep-alive until I looked... duh.
  • kuismakuisma Posts: 134
    edited 2011-03-05 07:40
    The client may still try using up to two sessions, even with HTTP/1.1 and keep-alive, but hopefully the browser will realize that the connected session works much better than the other one that keeps resetting ...
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-05 10:04
    I was hoping for a miracle, but Connection: keep-alive did not do the trick, I'm sorry to say. Firefox loaded but one out of the six images. Opera fared better with five of the six. Attached is the TCP dump for the Firefox session. 192.168.0.50:3456 is the Spinneret server, 192.168.0.6 is the Firefox user agent.

    -Phil
  • Mike GMike G Posts: 2,702
    edited 2011-03-05 10:31
    I spoke to soon and did not properly test. Wishful thinking I guess. Anyway, I can reproduce the problem with a keep-alive connection header.

    I'll have to keep my mouth shut until I test with due diligence.
  • kuismakuisma Posts: 134
    edited 2011-03-05 10:40
    Tricky. This basically implies that it is not possible using the W5100 and Spinneret for web servers.

    I would call this a browser bug, but they (Mozilla) already violates the RFC, so I guess they don't care ("single-user client SHOULD NOT maintain more than 2 connections with any server or proxy"). I.e. if in HTTP/1.1 and keep-alive, not succeeding opening more sessions, not using the already open session(s) for newly enqueued requests, is at least a misfeature.

    Firefox-3 may default use up to 6 concurrent sessions, and this is not possible to handle with the W5100 (except raw mode, and it is not possible to implement in the Propeller and expect to have space left for the user application).

    I do not see any workaround at present.
  • kuismakuisma Posts: 134
    edited 2011-03-05 10:52
    Using the data URL scheme, maybe? This way the main html file and the image data is contained in the same document, hence the same http/tcp session. This could be used for non-cachable images, and relying on the cache for the other images, and accepting [x] for some of the images the first time the page is loaded...? Dirty hack, indeed.

    http://en.wikipedia.org/wiki/Data_URI_scheme
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-05 11:35
    I thought about that, too, but I believe many browsers have a size limit and/or restrict to certain MIME types. I think I'll go back to my JS workaround for now and see if I can make it functional across the browser spectrum. If not, I guess I'll have to write a Base64 conversion method.

    -Phil
  • kuismakuisma Posts: 134
    edited 2011-03-05 12:26
    I turns out that Safari and Konqueror behave similarly to Firefox, and I think I know why. As long as JavaScript is in its while loop waiting for the load to complete, the browser is unable to update the DOM with the complete status. (Maybe it's even blocked from performing the image load.) I'm guessing that Opera is multi-threaded, which allows the browser to continue with its work while the JavaScript code is running. So I've either got to find a sleep function for unblocking the browser or else get out of loadimages and come up with a handler for image.onload.

    But that's for ma
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-05 12:30
    Here's something that works on Opera, Firefox, IE, and Safari. (Konqueror, not so much.) Rather than loading the images in a loop (and thus blocking their rendering in all but Opera), each call to loadimages loads one image and relies on the image's onload attribute to call loadimages again for the next one. Here's the HTML that does the job:
      <body onload=loadimages() style="background-color: #ccccaa">
        <script language=javascript>
          var i=0;
          function loadimages() {
            var p=document.images;
            if (i < p.length) {
              p[i].src = p[i].title;
              i++
            }
          }
        </script>
        ...
        
        <img title=/webcam.bmp?pan=5&target=8.00&view=7&map=Gray width=384 height=288 onload=loadimages() /><p/>
    
    Rather than requiring the onload=loadimages() in each <img> tag, I tried assigning it in the JS code: p.onload='loadimages()' before setting its src attribute. This worked for Opera, but not for the other browsers.

    Anyway, if anyone feels like trying it and providing feedback, the page is accessible here:

    -Phil
  • John AbshierJohn Abshier Posts: 1,116
    edited 2011-03-05 15:29
    Worked for me; loaded all images one at a time. I tried it on Opera and IE. It would be good it someone could try it with Chrome.

    John Abshier
  • kuismakuisma Posts: 134
    edited 2011-03-05 22:07
    Anyway, if anyone feels like trying it and providing feedback, the page is accessible here:

    I cannot even load http://phipi.homelinux.org:3456/webcam.bmp indicating you may chase the wrong bug. It starts loading, but never finish before it gets a reset, making my browser tell me the site is down (connection broken).

    This with Firefox 3.6.14 on GNU/Linux.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-06 12:56
    According to the TCP dump (attached), the entire image was transmitted and acknowledged, so I'm not sure why it did not get displayed in your browser or why it did not trigger subsequent image loads. I tried viewing it with Firefox 3.6.3 (Linux Mint) on my local network, and it worked without issue.

    I'm quite sure that the problem I've addressed was the issue, since it was also solved in Opera (pre-JavaScript) by setting the maximum connections per server to one.

    There is one puzzlement in the TCP dump that I'm at a loss to explain, though. And that's the huge number of packets containing one byte of data. Since complaining about this before, I've changed my program to buffer everything in a 512-character buffer and to send full buffers whenever possible. There's no way that the following code could revert to sending 500+ single characters in a row. (BTW, I don't see this happening on responses to my local network, only to those requests originating from outside; and then, only to some.)
    PUB out(char)
    
      array(@char, 1)
    
    PUB str(straddr)
    
      array(straddr, strsize(straddr))
    
    PUB array(addr, count)
    
      do_indent
      if (bufptr + count > BUF_SIZE)
        flush
      if (bufptr + count > BUF_SIZE)
        w5100.txTCP(0, addr, count)
      else
        bytemove(@buffer + bufptr, addr, count)
        bufptr += count 
    
    PUB outnz(char)
    
      if (char)
        out(char)
        
    PRI flush
    
      if (bufptr)
        w5100.txTCP(0, @buffer, bufptr)
        bufptr~
    
    PRI do_indent
    
      if (last_indent)      ' last_indent is non-zero only in HTML pages.
        last_indent~    
        ifnot (inpre)
          repeat indent
            str(string("  "))
    
    So there's something going on with txTCP or the W5100 chip itself that's causing things to dribble out this way.

    -Phil

    Addendum: I upgraded my Linux Firefox to 3.6.14. It still loads the entire page and all the images.
  • D.PD.P Posts: 790
    edited 2011-03-06 14:45
    Hi

    Here's a recent error from FF 3.6.15 Windows XP

    "The image “http://phipi.homelinux.org:3456/webcam.bmp” cannot be displayed, because it contains errors."

    I get a partially displayed image that's all.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-06 15:00
    What do you get when you right-click on the image, select "copy image location", and paste that location into the URL box? (BTW, I'm viewing the image okay in the same version of FF.)

    Thanks,
    -Phil
  • kuismakuisma Posts: 134
    edited 2011-03-06 22:49
    There is one puzzlement in the TCP dump that I'm at a loss to explain, though. And that's the huge number of packets containing one byte of data. Since complaining about this before, I've changed my program to buffer everything in a 512-character buffer and to send full buffers whenever possible. There's no way that the following code could revert to sending 500+ single characters in a row. (BTW, I don't see this happening on responses to my local network, only to those requests originating from outside; and then, only to some.)

    Sorry 'bout that. I believe it's a driver misfeature caused by my deadlock fix (still, one byte is better than deadlock ,-) and the fact that txTCP() is asynchronous.

    If I'm right, you'll fix this best by making your flush synchronous, not returning until the W5100 tx buffers are empty. This is the semantically sound behavior for your flush command. Or, if lazy, delay a short while after performing the txTCP().

    If/when we (I?) implement Nagles algorithm in the driver, this issue will be resolved.
  • ZootZoot Posts: 2,227
    edited 2011-03-06 22:52
    The bmp works inconsistenly in Safari and Firefox for Mac, for me anyway. Sometimes it's a decent b/w image, sometimes just a black box. Decent BMP support is inconsistent across browsers; have you tried writing this to the browser as a PNG or JPG?

    FYI, try this...
     p[i].onload=loadimages;
    

    ...to setup the "next" callback for loadimages. In JavaScript, as most event driven object-oriented languages, callbacks are objects, and you can't always pass params when "assigning" the function object to an event callback. The fact Opera interpreted the "string" 'loadImages()' as a callback function object is a lucky break, but I wouldn't expect that to work in most browsers.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-06 23:13
    Zoot,

    That works like a champ! Thanks!

    -Phil
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-06 23:57
    kuisma,

    If you don't mind indulging me, please give the site another try. I've made a temporary change to txTCP to test your idea:
          repeat while _size > 0
            'wait for room in the W5100 to send some of the data
            repeat until (freespace.word[0] [color=red]=> 1024[/color])
              readIND((_S0_TX_FSRO + (_socket * $0100)), @temp0, 2)
              freespace.byte[1] := temp0.byte[0]
              freespace.byte[0] := temp0.byte[1]
            chunksize := _size <# freespace.word[0]
    
    Thanks,
    -Phil
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-03-07 00:02
    Thanks. I didn't see any mini packets this time. Did the page and images load okay?

    -Phil
  • kuismakuisma Posts: 134
    edited 2011-03-07 00:03
    kuisma,

    If you don't mind indulging me, please give the site another try. I've made a temporary change to txTCP to test your idea:
          repeat while _size > 0
            'wait for room in the W5100 to send some of the data
            repeat until (freespace.word[0] [color=red]=> 1024[/color])
              readIND((_S0_TX_FSRO + (_socket * $0100)), @temp0, 2)
              freespace.byte[1] := temp0.byte[0]
              freespace.byte[0] := temp0.byte[1]
            chunksize := _size <# freespace.word[0]
    
    Thanks,
    -Phil

    Done.
    1 - Picture only - no RST this time, everything ok.
    2 - Main pake, loads fine. Js default disabled, no pictures.
    3 - Main page, JS enabled. Work just fine, all pictures eventually loads (quite slow).

    Your trace looks better?
Sign In or Register to comment.