Sub http_send_status (c, code, message As String)
    Dim s$
    s$ = "HTTP/1.1" + Str$(code) + " " + message + CRLF
    Put #Connections(c).handle, , s$
End Sub

Sub http_send_header (c, header As String, value As String)
    Dim s$
    s$ = header + ": " + value + CRLF
    Put #Connections(c).handle, , s$
End Sub

Sub http_end_headers (c)
    Put #Connections(c).handle, , CRLF
End Sub

Sub http_send_body (c, body As String)
    Put #Connections(c).handle, , body
End Sub

Sub http_do_get (c)
    Dim fh
    code = get_requested_filesystem_path(c)
    http_send_status c, 200, "OK"
    http_send_header c, "Connection", "close"
    http_end_headers c
    If InStr(code, "code=") Then
        http_send_body c, "<h1><strong>Authentication complete. You can close this window</strong></h1>"
    Else
        http_send_body c, "<h1><strong>Authentication failed. You can close this window</strong></h1>"
    End If
    close_connection c
End Sub

Sub http_do_head (c)
    Dim s$
    s$ = "HTTP/1.1 200 OK" + CRLF + CRLF
    Put #Connections(c).handle, , s$
End Sub

Sub close_connection (c)
    Close #Connections(c).handle
    Connections(c).handle = 0
End Sub

Function get_requested_filesystem_path$ (c)
    '7230 5.3 also 3986 for URI
    'Origin form only for now
    Dim raw_path As String
    raw_path = Connections(c).request_uri
    If Left$(raw_path, 1) <> "/" Then http_error 400, "Malformed URI", c

    Dim hash, questionmark, path_len
    hash = InStr(raw_path, "#") 'Clients shouldn't be sending fragments, but we will gracefully ignore them
    questionmark = InStr(raw_path, "?")
    path_len = Len(raw_path)
    If hash > 0 Then path_len = hash - 1
    If questionmark > 0 And questionmark < hash Then path_len = questionmark - 1
    ' Query strings are ignored for now

    get_requested_filesystem_path = cannonicalise_path(percent_decode(Left$(raw_path, path_len)))
End Function

Function percent_decode$ (raw_string As String)
    Dim final_string As String, hexchars As String
    Dim i, c
    For i = 1 To Len(raw_string)
        c = Asc(raw_string, i)
        If c = 37 Then '%
            hexchars = Mid$(raw_string, i + 1, 2)
            If Len(hexchars) = 2 And InStr("0123456789abcdefABCDEF", Left$(hexchars, 1)) > 0 And InStr("0123456789abcdefABCDEF", Right$(hexchars, 1)) > 0 Then
                final_string = final_string + Chr$(Val("&H" + hexchars))
            Else
                'String ends in something like "%1", or is invalid hex characters
                final_string = final_string + "%" + hexchars
            End If
            i = i + Len(hexchars)
        Else
            final_string = final_string + Chr$(c)
        End If
    Next i
    percent_decode = final_string
End Function


Function cannonicalise_path$ (raw_path As String)
    Dim path As String
    ReDim segments(1 To 1) As String
    Dim i, uplevels
    split raw_path, "/", segments()
    For i = UBound(segments) To 1 Step -1
        If segments(i) = "." Or segments(i) = "" Then
            _Continue
        ElseIf segments(i) = ".." Then
            uplevels = uplevels + 1
        Else
            If uplevels = 0 Then
                path = "/" + segments(i) + path
            Else
                uplevels = uplevels - 1
            End If
        End If
    Next i
    If path = "" Then path = "/"
    'Note: if uplevels > 0 at this point, the path attempted to go above the root
    'This is usually a client trying to be naughty
    cannonicalise_path = path
End Function

'https://www.qb64.org/forum/index.php?topic=1607.0
Sub split (SplitMeString As String, delim As String, loadMeArray() As String)
    Dim curpos As Long, arrpos As Long, LD As Long, dpos As Long 'fix use the Lbound the array already has
    curpos = 1: arrpos = LBound(loadMeArray): LD = Len(delim)
    dpos = InStr(curpos, SplitMeString, delim)
    Do Until dpos = 0
        loadMeArray(arrpos) = Mid$(SplitMeString, curpos, dpos - curpos)
        arrpos = arrpos + 1
        If arrpos > UBound(loadMeArray) Then ReDim _Preserve loadMeArray(LBound(loadMeArray) To UBound(loadMeArray) + 1000) As String
        curpos = dpos + LD
        dpos = InStr(curpos, SplitMeString, delim)
    Loop
    loadMeArray(arrpos) = Mid$(SplitMeString, curpos)
    ReDim _Preserve loadMeArray(LBound(loadMeArray) To arrpos) As String 'get the ubound correct
End Sub


Sub process_request (c)
    Dim eol
    Dim l As String
    Do
        eol = InStr(Connections(c).read_buf, CRLF)
        If eol = 0 Then Exit Sub
        l = Left$(Connections(c).read_buf, eol - 1)
        Connections(c).read_buf = Mid$(Connections(c).read_buf, eol + 2)
        If Connections(c).http_version = 0 Then 'First line not yet read
            process_start_line c, l
        Else
            If l = "" Then
                'headers complete; act upon request now
                Select Case Connections(c).method
                    Case HTTP_GET
                        http_do_get c
                    Case HTTP_HEAD
                        http_do_head c
                End Select
                Exit Sub
            Else
                process_header c, l
            End If
        End If
    Loop
End Sub

Sub process_start_line (c, l As String)
    '7230 3.1.1
    'METHOD uri HTTP/x.y
    Dim sp1, sp2
    sp1 = InStr(l, " ")
    If sp1 = 0 Then http_error 400, "Bad Request", c

    '7231 4.3
    Select Case Left$(l, sp1 - 1)
        Case "GET"
            Connections(c).method = HTTP_GET
        Case "HEAD"
            Connections(c).method = HTTP_HEAD
        Case Else
            http_error 501, "Not Implemented", c
    End Select

    sp2 = InStr(sp1 + 1, l, " ")
    If sp2 = 0 Or sp2 - sp1 = 1 Then http_error 400, "Bad Request", c
    Connections(c).request_uri = Mid$(l, sp1 + 1, sp2 - sp1 - 1)

    '7230 2.6
    If Mid$(l, sp2 + 1, 5) <> "HTTP/" Then
        http_error 400, "Bad Request", c
    End If
    Select Case Mid$(l, sp2 + 6)
        Case "1.0"
            Connections(c).http_version = HTTP_10
        Case "1.1"
            Connections(c).http_version = HTTP_11
        Case Else
            http_error 505, "HTTP Version Not Supported", c
    End Select
End Sub

Sub process_header (c, l As String)
    'All headers ignored for now
End Sub

Sub http_error (code, message As String, connection)
    http_send_status connection, code, message
    http_send_header connection, "Content-Length", "0"
    http_send_header connection, "Connection", "close"
    http_end_headers connection
    close_connection connection
    Http_error_info.code = code
    Http_error_info.message = message
    Http_error_info.connection = connection
    Error 100
End Sub

