From 79b1624e8b3ef486c58253075e0f7f65c0414a5a Mon Sep 17 00:00:00 2001 From: Remy Sharp Date: Sun, 25 Apr 2021 22:22:54 +0100 Subject: [PATCH] connection: keep-alive Instead of requesting the connection: close - which it appears some ESPs will close the connection during buffering the uart data, now we request with connection: keep-alive and manually close the TCP connection when we're done. This is achieved by parsing the http headers, looking for content-length converting the value to a 32bit value, then subtracting the chunks we get from the uart until the length reaches zero. Chunked encoding is still going to be a problem because the bytes include "metadata" that says how much data we've got in a chunk so the length won't match the number of bytes coming down, but chunked encoding wasn't supported yet anyway, so it's still an open issue. This has been tested on AT versions: - 1.1.0.0 - 1.2.0.0 - 1.3.0.0 - 1.6.0.0 (which had the problem in the first place) --- example/capture-esp.bas | Bin 748 -> 825 bytes example/capture-esp.bas.txt | 19 ++- http | Bin 5065 -> 5398 bytes http-debug.dot | Bin 5097 -> 5434 bytes src/headers.asm | 283 ++++++++++++++++++++++++++++++++++-- src/main.asm | 31 +++- src/messages.asm | 1 + src/strings.asm | 13 -- src/utils.asm | 46 ++++++ src/version.inc.asm | 2 +- src/wifi.asm | 37 +---- 11 files changed, 363 insertions(+), 69 deletions(-) delete mode 100644 src/strings.asm diff --git a/example/capture-esp.bas b/example/capture-esp.bas index 7dc5cab96d578e9bda5a5f250675587de91c35b2..4f73e09fa06be389a8b730221123d66669683f97 100644 GIT binary patch delta 245 zcmaFEx|2;Tz$Y}=*u_6sijl#RnSp^}Clf=%PNs>9aT5)=8COkg(4M&0o6XRGkAdOS zWIo0uH46g+5XV8D!OB`8DKRfQGcTQ&MSzcCLjM~>W3ZeEE5qcOjCP4EyLcGhm>O7u z)ISnt;AItI2CCv^l{07fx<<7~$IuWc!Og&6eHX-F1TvJ;5;Jp@tnY$!0D-lVoHJ delta 183 zcmdnV_J&m~z$Y}=*u_6sijmUO&wy8iYqAfM&gAt>T>zKPFyR0I diff --git a/example/capture-esp.bas.txt b/example/capture-esp.bas.txt index c48c5e1..1bda3b3 100644 --- a/example/capture-esp.bas.txt +++ b/example/capture-esp.bas.txt @@ -2,22 +2,25 @@ 1 RUN AT 3 10 LAYER 0 20 BANK 20 ERASE -30 ON ERROR GO SUB 9000 40 CLS : PRINT INVERSE 1;"HTTP 4k debug"; INVERSE 0 -145 t$="4k":u$="/10":%r=1: GO SUB 1000 +145 t$="4k":u$="/10":%r=1: GO SUB 1010 900 ON ERROR 910 PRINT FLASH 1;"All OK" 920 STOP 1010 PRINT AT %r,0;t$+" test..." 1020 t$=t$+".bin" -1030 IF %e=1 THEN GO TO 7000: ; banking -1040 REM file test logic +1030 IF %e=1 THEN GO TO 8000: ; banking +1040 ON ERROR GO TO 1300 1200 ../http-debug.dot -h data.remysharp.com -u u$ -f t$ -v 6 +1210 GO TO 5090 +1300 ON ERROR +1310 PRINT AT %r,11; INVERSE 1;"fail"; INVERSE 0;". Verifying: " +1320 GO TO 5110 5090 PRINT AT %r,11;"done. Verifying: " -5100 c$=t$+" -1 -mb 20" -5110 .$ extract c$ -5120 %i=% BANK 20 PEEK 0 -5130 SAVE "4k-esp-bank.bin" BANK 20,%$2000,%$2000 +5110 SAVE "4k-esp-bank.bin" BANK 20,%$2000,%$2000 +5115 c$=t$+" -1 -mb 20" +5120 .$ extract c$ +5130 %i=% BANK 20 PEEK 0 8000 IF %i=$FF THEN PRINT AT %r,27; INVERSE 1;"OK"; INVERSE 0: ELSE PRINT AT %r,27; FLASH 1;"BAD": PAUSE 0: STOP 8010 RETURN 9000 ON ERROR diff --git a/http b/http index 853923c15cd1e8a18609e4845da8433aba13deb1..06f40e456640c09b6e8eae3f0d013e956fb2c719 100644 GIT binary patch delta 2951 zcmb6be^3GdfNMwZxeBUjQ-8=5bE+UsjP7zQEA_g#NM~rn0qfJuHL?bxG zJN`(gGfn?7*QV37Njlojb!?}^rY23}Ohtw%Yx+s4;KanMz0|u&YgR=NF~ZXK?VZ}D z?H}End%xfN-uvF}`#$y2*E~_qg7Yee$MAF_wUA2qnM5U{?N4K6W4D~ZWWT`Hm4~f_ z4W!NTb*vq+n#tn|23%vn5(-_*SCVrw&Mzl-W%8MfXWiVgN!P3!mLS`MuDe>yc!qw;nZdiM%~LIo^y#+H5|*)X z7A2RLZ9F)U_0kX(Tl+59Hk?40xH&as6M^W!0OFDR^ZLB`Qcl9y%3 zg{JawHBG@%MV_qEwuMBJBYS2gWDk{-M?APjT*WH+6*`L?m>Gb~v{o=F4k$HZ9ND=K z@o#DMgSEF(iW>Lr zJ@9qP8P|u4m?z_3Mt7{87DEWgvIrz@cEpM^Sdj!>{>=DlEHZ_Ik7u~N3 z*vz<(3rRI~3p0!t1pOz4+^MAMStAl?7Q+BP?FEyJId=tXvG3Z}Mqh>(t5EB626NfE zhqOMjOHfHn;4~n}%feHJGHpT@?TLG;>oi>!ewoKrK~4#Hvsh{&PA_L^=xPo+?Bu8? zv>)#hOY^BPzoE<9*s`Uwx#P*^j{1xm4~V4>;*5GZM@HMYslBt=@Wihhs{5L@tzXGH zg%EyMEOpYD&Wu-6%jWg%_q3+CN-l~a^KgTRV->ZkqCh)Vgjrb;@inp3M*VEy*U{PK zuydwbl`KRBeZ64fZj%K;Z$URGLg?=lZ9tzAt2eIVM?Eyr``J9mQ=LrV$_jU#h#M;0 z^&;N#zYOuSp3*`ZqA&?OnNcb`CIu*2{&nR-Bq>(A5JJ4hXoujB7{UO7h1_Mu>NGlff26Td= z7IfUGe-k=Jwe?x89oD0b>7lE zns;8dvgXZA^~n_J^F645t`$aihLSE#iWC%nGi^pqsgZbJ{LB&Wp{Sk%NU`y8VX1CH zIfL?bX;8i<)nZK|6Tz!SCjSnG^5CJt_`VZp5ccLvDNXzzDMg|qP}UHi2cc$YC_H8q zS5)<%WsOh5Wa9b%R$CHgrIqf_aJcG0;Sdkc(QtGJyuw;&pb0)KU|NMTy%XNA1tF?8 zqfe-)2~AMsKm^{`8qsK!1D&QaJ9__ttOfm&%C?}NWxU=5M0WbzlBmFZKC|6-6~%qrD2B_;YbxnCa$bwm9N!VuR|pw}Z)qKY=>u@Q1q z3Y!z(hZbntsznRauDwmCo#{-CFEz=U8QPoTzfF%0 zw$5mfdiXcOL#c~N*JU61z;_W}_C@sIX`Z_i4)&_-jgQEL5dt8Dn4lphVn1}WMN9ls zDlD6T|0Gvgm}pA0r4pj{tnud)-bC(@_PD`(E3q%QBvFo7zW72=w};LT(c#RD&||j? zj$L`1pORZC+3g>K{fHySp>NCE{o1QttF>3Rs^pwM!%^aC5N}oMSY|C85H5CL6{Eea z-cq(vYo(hiX#`=JvfaOfXny5s15K%k)Cf!!mLXSSyO*Kp6@PMkD3P2}wL>{tpXymu ztO3iivDal=Kvisku{R9(CWZ0Qu^&Oo_*^Bq?u!q9f}!;{F;@t$4xpuYd&OWaNZ}Z` z6yZr%a8%zdT+&|Dc?u5^i%K zIlL~A#4lxvfT^U=sieksu3+@0L=H+tMeSCr#r%^E%2b9OkTvO0QBnFvyE2`T z&=k|o6EFys87j#toX+&=6EGoF=6Lq>%@FrF<~_KjXdUiZ=}+%@<|G%(05c~EtP1k} zlTj7lmEapJ(RYSS7m(hF_86Q)_=oO8GykG-2p{R2P*+y_$Ej?O9fm9Aj9-S$(w|cdCvP>o z1jY*HtJxg-{b0G8ID)rB^>tHHK&;S eTu;vnzMap1{a~ylzbW=aL0{}^1-4^q0sBu^E`jC% delta 2578 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 diff --git a/src/headers.asm b/src/headers.asm index 90aed8f..db349bc 100644 --- a/src/headers.asm +++ b/src/headers.asm @@ -1,25 +1,237 @@ + ; DEFINE TEST_HEADERS + IFDEF TEST_HEADERS + OPT reset --zxnext --syntax=abfw + INCLUDE "constants.inc.asm" + DEVICE ZXSPECTRUM48 + SLDOPT COMMENT WPMEM, LOGPOINT, ASSERTION + + ORG $8000 +start: + di + exx + ld hl, s2 + exx + call Headers.findContentLength + + ld de, 1299 + call Headers.contentLengthSub + + ; call Uart.read + + jr $ + + INCLUDE "utils.asm" + + MODULE Uart +; Fake Uart.read +; +; A <- result +; Modifies: BC +read: + exx + ld a, (hl) + inc hl + exx + ret + ENDMODULE + + +result: + DS 256 + ENDIF + + MODULE Headers + + DEFB $AA +contentLength: ; 32bit result + DISPLAY "Header.contentLength @ ",/H,$ + DWORD 0 + + DEFB $AA +buffer: + DISPLAY "Header.buffer @ ",/H,$ + BLOCK 9, 0 + +; Subtracts DE from contentLength +; +; DE=integer +; Fz=all bytes consumed +; Fc=error +; Modifies: AF, HL, DE +contentLengthSub: + or a ; reset carry for sbc + ld hl, (contentLength) ; do the LSW first + sbc hl, de + ld (contentLength), hl + ret nc + + or a + ld hl, (contentLength+2) + ld de, 1 + sbc hl, de + ld (contentLength+2), hl + jr z, .checkZero + ret +.checkZero + ld hl, (contentLength) + ld a, h + or l + ret + + +; Takes DE and searches the text for the content length setting it in (HL) +; searching through the headers for `content-length: nnnn`. If successful +; carry is clear and the Header.contentLength is set to 32bits. +; This reduces DE as it works through the Uart.read +; +; DE=length of UART buffer +; Fc=failed to find header +; Modifies: AF, BC, DE, HL, IX +findContentLength + ld hl, buffer + + ;; process a single header +.processHeader + call Uart.read ; load A with the next character + dec de + + ;; convert character to uppercase + cp 'a' ; if A < 'a' then skip case shift + jr c, .noCaseShiftC + sub $20 +.noCaseShiftC + + ;; narrow down what we're looking for, ref: + ;; https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers + cp 'C' + jr nz, .TestForLineEndingHeader + + ;; slurp to the `-` character +.findDash + call Uart.read + dec de + cp CR ; always check if we have an EOL + jr z, .processHeader + + cp '-' ; now check for the char we want + jr nz, .findDash + + call Uart.read ; after `-` we're looking for L + dec de + + ;; shift case + cp 'a' ; if A < 'a' then skip case shift + jr c, .noCaseShiftL + sub $20 +.noCaseShiftL + cp CR + jr z, .processHeader ; is this EOL? + + cp 'L' ; L? + jr nz, .slurpToEndOfHeader + + call Uart.read ; E? + dec de + cp CR ; EOL? + jr z, .processHeader + + cp 'a' ; if A < 'a' then skip case shift + jr c, .noCaseShiftE + sub $20 +.noCaseShiftE + + cp 'E' ; this now the content-length + jr nz, .slurpToEndOfHeader + +.findColon + call Uart.read + dec de + cp CR ; always check if we have an EOL + jr z, .processHeader + + cp ':' + jr nz, .findColon + +.eatSpaces + call Uart.read + dec de + cp CR ; always check if we have an EOL + jr z, .processHeader + + cp ' ' + jr z, .eatSpaces + + ;; now we have our numbers - store this in a buffer for later conversion + ld (hl), a + inc hl +.captureNumeric + call Uart.read + dec de + cp CR ; always check if we have an EOL + jr z, .convertToUint32 + + ld (hl), a + inc hl + jr .captureNumeric + +.convertToUint32 + push de + ld de, buffer + call atoui32 + ; CSP_BREAK + ld (contentLength), ix + ld (contentLength+2), hl + pop de + jp .slurpToEndOfAllHeaders + +.TestForLineEndingHeader + ;; this could actually be a blank line in which case we need to return + cp CR + jr nz, .slurpToEndOfHeader + ;; this was a CR and we've not found the content length + + ;; flush the next character: LF + call Uart.read + dec de + scf + ret + +.slurpToEndOfHeader + call Uart.read : dec de : cp CR : jr nz, .slurpToEndOfHeader + call Uart.read : dec de ; LR + jp .processHeader + +.slurpToEndOfAllHeaders + call Uart.read : dec de : cp CR : jr nz, .slurpToEndOfAllHeaders + call Uart.read : dec de ; LR + call Uart.read : dec de : cp CR : jr nz, .slurpToEndOfAllHeaders + call Uart.read : dec de ; LR + ret +Parse + + Post - ld hl, Strings.post + ld hl, HeaderStrings.post ld bc, 5 jr method Get - ld hl, Strings.get + ld hl, HeaderStrings.get ld bc, 4 method ldir ret GetTrailer - ld hl, Strings.reqTail - ld bc, Strings.reqTailLen + ld hl, HeaderStrings.reqTail + ld bc, HeaderStrings.reqTailLen ldir ret PostTrailer - ld hl, Strings.reqTail - ld bc, Strings.postLen + ld hl, HeaderStrings.reqTail + ld bc, HeaderStrings.postLen ldir ret @@ -29,7 +241,7 @@ PostTrailer ; Modifies: AF Host push hl - ld hl, Strings.host + ld hl, HeaderStrings.host ld bc, 6 ldir pop hl @@ -50,22 +262,73 @@ copyHLtoDE jr copyHLtoDE NewLine - ld hl, Strings.newLine + ld hl, HeaderStrings.newLine ld bc, 2 ldir ret EndPost - ld hl, Strings.emptyLine + ld hl, HeaderStrings.emptyLine ld bc, 5 ldir ret EndGet - ld hl, Strings.newLine + ld hl, HeaderStrings.newLine ld bc, 3 ldir ret ENDMODULE + + MODULE HeaderStrings +emptyLine DEFB CR, LF +newLine DEFB CR, LF, 0 +get DEFB "GET " +post DEFB "POST " +host DEFB "Host: " +reqTail DEFB " HTTP/1.1", CR, LF, "Connection: keep-alive", CR, LF +reqTailLen EQU $-reqTail +postLength DEFB "Content-Type: application/x-www-form-urlencoded", CR, LF, "Content-Length: " +postLen EQU $-reqTail + + ENDMODULE + + + IFDEF TEST_HEADERS + +s1: DEFB "HTTP/1.1 200 OK",CR,LF + DEFB "Server: nginx/1.14.2",CR,LF + DEFB "Date: Fri, 23 Apr 2021 18:42:46 GMT",CR,LF + DEFB "Content-Type: text/html",CR,LF + DEFB "Content-Length: 6608",CR,LF + DEFB "Last-Modified: Fri, 10 Jul 2020 15:43:55 GMT",CR,LF + DEFB "Connection: keep-alive",CR,LF + DEFB "Permissions-Policy: interest-cohort=()",CR,LF + DEFB "Referrer-Policy: no-referrer",CR,LF + DEFB "Accept-Ranges: bytes",CR,LF + DEFB CR,LF + DEFB "",0 + +s2: + DEFB "HTTP/1.1 200 OK",CR,LF + DEFB "Content-Type: application/octet-stream",CR,LF + DEFB "Content-Length: 65536",CR,LF + DEFB "Date: Fri, 23 Apr 2021 22:22:26 GMT",CR,LF + DEFB "Connection: keep-alive",CR,LF + DEFB "Keep-Alive: timeout=5",CR,LF + DEFB CR,LF + DEFB "",0 +s3: + DEFB "HTTP/1.1 200 OK",CR,LF + DEFB "Content-Type: application/octet-stream",CR,LF + DEFB "Date: Fri, 23 Apr 2021 22:22:26 GMT",CR,LF + DEFB "Connection: keep-alive",CR,LF + DEFB "Keep-Alive: timeout=5",CR,LF + DEFB CR,LF + DEFB "",0 + + + SAVESNA "headers.sna", start + ENDIF diff --git a/src/main.asm b/src/main.asm index 760fe11..d8f1a01 100644 --- a/src/main.asm +++ b/src/main.asm @@ -29,7 +29,7 @@ testFakeArgumentsLine ; DZ "get -f demo.scr -h data.remysharp.com -u /5 -v 2" ; DZ "get -b 5 -o -0 -h data.remysharp.com -u /5 -v 2" ; DZ "get -f http-demo.tap -h zxdb.remysharp.com -u /get/18840 -v 2" - ; DZ "get -f output.bin -h 192.168.1.118 -u /output.bin -p 5000 -v 3" + ; DZ "get -f 4k.bin -h data.remysharp.com -u /10 -v 3" ; DZ "post -b 21 -h data.remysharp.com -u /1 -f 3 -l 16384 -7" ENDIF @@ -309,10 +309,19 @@ Get LoadPackets call Wifi.getPacket - ld a, (Wifi.closed) - and a - jr nz, Exit + ;; reduce the content length left to read + ld de, (Wifi.bufferLength) + call Headers.contentLengthSub + ld hl, (Headers.contentLength) + jr nz, .continue + jr c, .contentLenghtError + ;; FIXME this is lazy coding + call Wifi.closeTCP + ld hl, Wifi.closed + ld (hl), 1 + +.continue ;; now write to file if required ld a, (State.fileMode) cp WRITE_TO_FILE @@ -323,8 +332,17 @@ LoadPackets ld bc, (Wifi.bufferLength) call esxDOS.fWrite .skipFileWrite + + ld a, (Wifi.closed) + and a + jr nz, Exit + jr LoadPackets +.contentLenghtError: + ld hl, Err.contentLength + jp Error + ; HL = pointer to error string Error xor a ; set A = 0 - TODO is this actually needed? @@ -364,7 +382,6 @@ Exit INCLUDE "bank.asm" INCLUDE "file.asm" INCLUDE "parse.asm" - INCLUDE "strings.asm" INCLUDE "base64.asm" INCLUDE "headers.asm" @@ -397,8 +414,8 @@ diagBinPcLo EQU ((100*diagBinSz)%8192)*10/8192 IFDEF LAUNCH_CSPECT : IF ((_ERRORS = 0) && (_WARNINGS = 0)) ;; delete any autoexec.bas SHELLEXEC "(hdfmonkey rm /Applications/cspect/app/cspect-next-2gb.img /nextzxos/autoexec.bas > /dev/null) || exit 0" - SHELLEXEC "hdfmonkey put /Applications/cspect/app/cspect-next-2gb.img http-debug.dot /dot/http" - SHELLEXEC "mono /Applications/cspect/app/cspect.exe -r -w5 -basickeys -zxnext -nextrom -exit -brk -tv -mmc=/Applications/cspect/app/cspect-next-2gb.img -map=./http.map -sd2=/Applications/cspect/app/empty-32mb.img"; -com='/dev/tty.wchusbserial1420:11520'" + SHELLEXEC "hdfmonkey put /Applications/cspect/app/cspect-next-2gb.img http-debug.dot /devel/http-debug.dot" + SHELLEXEC "mono /Applications/cspect/app/cspect.exe -r -w5 -basickeys -zxnext -nextrom -exit -brk -tv -mmc=/Applications/cspect/app/cspect-next-2gb.img -map=./http.map -sd2=/Applications/cspect/app/empty-32mb.img -com='/dev/tty.wchusbserial1430:11520'" ENDIF : ENDIF DISPLAY "TEST BUILD" ENDIF diff --git a/src/messages.asm b/src/messages.asm index 714a06e..16c7449 100644 --- a/src/messages.asm +++ b/src/messages.asm @@ -18,6 +18,7 @@ borderError DC "G Border out of range 0-7" hostError DC "H Host required" noFileOrBank DC "I Filename or bank must be specified" fileOpen DC "J Could not open file for writing" +contentLength DC "K Content length error" ENDMODULE MODULE Msg diff --git a/src/strings.asm b/src/strings.asm deleted file mode 100644 index 1ce6470..0000000 --- a/src/strings.asm +++ /dev/null @@ -1,13 +0,0 @@ - - MODULE Strings -emptyLine DEFB CR, LF -newLine DEFB CR, LF, 0 -get DEFB "GET " -post DEFB "POST " -host DEFB "Host: " -reqTail DEFB " HTTP/1.1", CR, LF, "Connection: close", CR, LF -reqTailLen EQU $-reqTail -postLength DEFB "Content-Type: application/x-www-form-urlencoded", CR, LF, "Content-Length: " -postLen EQU $-reqTail - - ENDMODULE diff --git a/src/utils.asm b/src/utils.asm index 7430943..9b9b815 100644 --- a/src/utils.asm +++ b/src/utils.asm @@ -138,3 +138,49 @@ strEnd: pop af ret +atoui32: +;=============================================================== +;Input: +; DE points to the base 10 number string in RAM. +;Outputs: +; HLIX is the 32-bit value of the number +; DE points to the byte after the number +; BC points to the start of the number +; z flag means it ended on a decimal point. +;Destroys: +; A (actually, add 30h and you get the ending token) +; BC +;=============================================================== + ld hl,0 + ld ix,0 + push de ;save the pointer + jr .start +.loop: + inc de + ld b,ixh + ld c,ixl + push hl + add ix,ix + adc hl,hl + add ix,ix + adc hl,hl + inc ix + dec ix + add ix,bc + pop bc + adc hl,bc + add ix,ix + adc hl,hl + add a,ixl + ld ixl,a + jr nc,.start + inc ixh + jr nz,.start + inc hl +.start: + ld a,(de) + sub 30h + cp 10 + jr c,.loop + pop bc + ret diff --git a/src/version.inc.asm b/src/version.inc.asm index cbad7d4..f9162d1 100644 --- a/src/version.inc.asm +++ b/src/version.inc.asm @@ -1,5 +1,5 @@ IFNDEF TESTING - DEFINE VERSION "1.0-19b" + DEFINE VERSION "1.0-20b" ELSE DEFINE VERSION "TEST" ENDIF diff --git a/src/wifi.asm b/src/wifi.asm index 534b7b9..9d7aeb1 100644 --- a/src/wifi.asm +++ b/src/wifi.asm @@ -68,6 +68,10 @@ RetartESP: ret +closeTCP + EspCmdOkErr "AT+CIPCLOSE" + ret + ; HL - host pointer ; DE - port pointer openTCP: @@ -380,14 +384,9 @@ getPacket: ld a, (firstRead) and a : jr z, .headerProcessed -.searchForBlankLine - ;; since we're reading HTTP responses, the header isn't interesting to - ;; us so we'll look for \n\r\n\r in a row - call Uart.read : dec de : cp CR : jr nz, .searchForBlankLine - call Uart.read : dec de ; LR - call Uart.read : dec de : cp CR : jr nz, .searchForBlankLine - call Uart.read : dec de ; LR - +.processHeader + ;; we're searching for "content-length:" + call Headers.findContentLength ld a, 0 ld (firstRead), a @@ -415,28 +414,6 @@ getPacket: ld a, ixh add de, a -; IFDEF TESTING -; and a -; call nz, .captureIXState -; jr .readp -; .captureIXState -; ld iyh, d -; ld iyl, e -; exx -; ld a, ixh -; ld (hl), a -; dec hl - -; ld d, iyh -; ld e, iyl - -; ld (hl), e -; dec hl -; ld (hl), d -; dec hl -; exx -; ret -; ENDIF .readp ld a, h cp HIGH Bank.buffer