I haven't mentioned the other server I wrote, GLV-1.12556 [1]. I wrote it a few months ago mainly as a means to test out the Lua TLS (Transport Layer Security) wrapper [2] I wrote [3] beacuse otherwise, the wrapper is just an intellectual exercise. It implements the Gemini protocol [4] which lies somewhat between gopher and HTTP (HyperText Transport Protocol).
One issue keeps rearing its ugly head—files larger than some size just aren't transfered. It just causes an error that I haven't been able to figure out. The first time this happened several months ago, I hacked at the code and thought I got it working. Alas, it's happening again. I received word today of it failing to send files beyond a certain size, and yes, I can reproduce it.
But here's the kicker—I can reproduce it on my live server [5] but I can't reproduce it locally. It only seems to happen across the Internet. So any testing now has to happen “live” (as it were) on the “production server” (grrrrrr). I fixed some possible issues, maybe, like this bit of code:
ios._drain = function(self,data) local bytes = self.__ctx:write(data) if bytes == tls.ERROR then -- -------------------------------------------------------------------- -- I was receiving "Resource temporarily unavailable" and trying again, -- but that strategy fails upon failure to read a certificate. So now -- I'm back to returning an error. Let's hope this works this time. -- -------------------------------------------------------------------- return false,self.__ctx:error() elseif bytes == tls.WANT_INPUT or bytes == tls.WANT_OUTPUT then self.__resume = true coroutine.yield() return self:_drain(data) elseif bytes < #data then nfl.SOCKETS:update(conn,"w") self.__resume = true coroutine.yield() return self:_drain(data:sub(bytes+1,-1)) end return true end
Upon looking over this code, I rethought the logic if dealing with tls.WANT_INPUT (the TLS layer needs the underlying socket descriptor to be readable) or tls.WANT_OUTPUT (the TLS layer needs the underlying socket descriptor to be writable) with the same bit of code, and rewrote it thusly:
ios._drain = function(self,data) local bytes = self.__ctx:write(data) if bytes == tls.ERROR then -- -------------------------------------------------------------------- -- I was receiving "Resource temporarily unavailable" and trying again, -- but that strategy fails upon failure to read a certificate. So now -- I'm back to returning an error. Let's hope this works this time. -- -------------------------------------------------------------------- return false,self.__ctx:error() elseif bytes == tls.WANT_INPUT then self.__resume = true coroutine.yield() return self:_drain(data) elseif bytes == tls.WANT_OUTPUT then nfl.SOCKETS:update(conn,"rw") self.__resume = true coroutine.yield() return self:_drain(data) elseif bytes < #data then nfl.SOCKETS:update(conn,"rw") self.__resume = true coroutine.yield() return self:_drain(data:sub(bytes+1,-1)) end return true end
Now, upon receiving a tls.WANT_OUTPUT it updates the events on the underlying socket descriptor from “read ready” (which is always true) to “read and write ready.” But even that didn't fix the issue.
I then spent the time trying to determine the threshhold, creating files of various sizes until I got two that differed by just one byte. Any file that has 11,466 bytes or less will get served up. Any file that is 11,467 bytes or more, the connection is closed with the error “Resource temporarily unavailable.” I have yet to figure out the cause of that. Weird.
[1] https://github.com/spc476/GLV-1.12556
[2] https://github.com/spc476/lua-conmanorg/blob/master/src/tls.c
[4] gopher://zaibatsu.circumlunar.space:70/0/~solderpunk/gemini/docs/spec-spec.txt