
struc http_status resp, extfile, body {
common
  local ..resp, ..resplen, ..extf, ..extflen

  .response dd ..resp
  .resplen  dd ..resplen

  if extfile eqtype 'A'
    .extfile  dd ..extf
    .extlen   dd ..extflen
  else
    .extfile  dd 0
    .extlen   dd 0
  end if

  if body eq NONE
    .body dd 0
    .bodylen  dd 0
  else
    if body eq DEFAULT
      .body dd -1
      .bodylen  dd 0
    else
      .body dd body
      .bodylen  dd body#\.length
    end if
  end if

  ..resp db resp, 13, 10
  ..resplen = $ - ..resp
         db 0

  if extfile eqtype 'A'
    ..extf db extfile
    ..extflen = $ - ..extf
           db 0
  end if
}

virtual at 0
http_status http_status "1234", "1234", NONE
end virtual


iglobal
  headerHTTP200 http_status 'HTTP/1.1 200 OK',                  0,          NONE
  headerHTTP400 http_status 'HTTP/1.1 400 Bad request',         '400.html', pageHTTP400
  headerHTTP403 http_status 'HTTP/1.1 403 Forbidden',           '403.html', pageHTTP403
  headerHTTP404 http_status 'HTTP/1.1 404 Not Found',           '404.html', pageHTTP404
  headerHTTP405 http_status 'HTTP/1.1 405 Method Not Allowed',  '405.html', pageHTTP405

  pageHTTP400   file  'help_server/400.html'
  .length = $ - pageHTTP400
                dd 0

  pageHTTP403   file  'help_server/403.html'
  .length = $-pageHTTP403
                dd 0

  pageHTTP404   file  'help_server/404.html'
  .length = $-pageHTTP404
                dd 0

  pageHTTP405   file  'help_server/405.html'
  .length = $-pageHTTP405
                dd 0

 cCRLF db 13, 10, 0

endg



; Process user requests
; arguments:
;   hSocket - the socket handler of the accepted connection.
;   pHeaders - array with strings containing all received headers, including the request line.
proc ProcessRequest, .hSocket, .pHeaders, .pPostData
  .method    dd ?
  .hFileName dd ?
  .query     dd ?
  .protocol  dd ?
begin
        pushad

        mov     [.method],    0
        mov     [.hFileName], 0
        mov     [.query],     0
        mov     [.protocol],  0


; Debug code
if defined options.DebugMode & ~options.DebugMode = 0
        mov     esi, [.pHeaders]
        mov     ecx, [esi+TArray.count]
.loop:
        dec     ecx
        js      .end_loop
        stdcall FileWriteString, [STDOUT], [esi+TArray.array]
        stdcall FileWriteString, [STDOUT], cCRLF
        add     esi, 4
        jmp     .loop

.end_loop:
; end debug code.
end if

        mov     esi, [.pHeaders]
        cmp     [esi+TArray.count], 0
        je      .bad_request

        stdcall StrLen, [esi+TArray.array]
        mov     ecx, eax
        stdcall StrPtr, [esi+TArray.array]      ; first string is the http request.
        mov     edi, eax

        cmp     ecx, 4
        jb      .bad_request    ; 400, bad request

        mov     eax, [edi]
        lea     edi, [edi+4]
        sub     ecx, 4

        mov     [.method], eax   ; the requests are limit to 4 bytes.

        mov     al, ' '
        repe scasb

        dec     edi

        cmp     byte [edi], '/'
        jne     @f
        inc     edi
        dec     ecx
@@:
        mov     esi, edi  ; scan to the first space (notice, that the path should not contain any spaces.
        repnz scasb

        mov     edx, edi
        sub     edx, esi
        dec     edx

        test    edx, edx
        jnz     .copy_fn

        stdcall StrNew
        stdcall StrCat, eax, 'index.md'
        jmp     .filename_ok

.copy_fn:
        stdcall StrExtract, esi, 0, edx

.filename_ok:
        mov     [.hFileName], eax

        jecxz   .protocol_ok

        stdcall StrExtract, edi, 0, ecx
        mov     [.protocol], eax

.protocol_ok:
        cmp     [.method], 'GET '
        je      .request_ok

        cmp     [.method], 'POST'
        je      .request_ok

        cmp     [.method], 'HEAD'
        je      .head_method

; 405, method not allowed.
        stdcall SendResponse, headerHTTP405
        jmp     .finish

; 400, bad request
.bad_request:
        stdcall SendResponse, headerHTTP400
        jmp     .finish

; 200, OK + HEAD
.head_method:
        stdcall SendResponse, headerHTTP200
        jmp     .finish


.request_ok:
        stdcall ProcessFileRequest, [.hSocket], [.hFileName], [.pPostData]

.finish:
        stdcall StrDel, [.hFileName]
        stdcall StrDel, [.query]
        stdcall StrDel, [.protocol]
        popad
        return
endp



hdrServer       text 'Server: Fresh Help server', 13, 10
hdrConnection   text 'Connection: close', 13, 10, 'Cache-Control: no-cache,no-store,max-age=0,must-revalidate', 13, 10


proc SendResponse, .hSocket, .response
begin
        pushad

        mov     esi, [.response]
        stdcall SocketSend, [.hSocket], [esi+http_status.response], [esi+http_status.resplen], 0
        jc      .finish

        stdcall SocketSend, [.hSocket], hdrServer, hdrServer.length, 0
        jc      .finish

        stdcall SocketSend, [.hSocket], hdrConnection, hdrConnection.length, 0
        jc      .finish

.send_body:
        cmp     [esi+http_status.extfile], 0
        je      .try_default

        stdcall LoadBinaryFile, [esi+http_status.extfile]
        jc      .try_default

        push    eax
        stdcall SocketSendStr, [.hSocket], cCRLF
        jc      .finish
        stdcall SocketSend, [.hSocket], eax, ecx, 0
        stdcall FreeMem ; from the stack
        jmp     .finish

.try_default:
        cmp     [esi+http_status.body], 0
        je      .end_ok

        stdcall SocketSendStr, [.hSocket], cCRLF
        stdcall SocketSend, [.hSocket], [esi+http_status.body], [esi+http_status.bodylen], 0
        jc      .finish

.end_ok:
        clc

.finish:
        popad
        return
endp




