http-client.bas at [1c53722afc]

File libs/http-client.bas artifact 4ea818dc9f part of check-in 1c53722afc


#INCLUDE "winhttp.bi"
#INCLUDE "http-client.bi"

'Implement friendly interfaces to interact with WinHTTP api

CONST AS STRING IguanaUserAgent = "IguanaCMS HTTP Client/1.0"

DIM SHARED AS ULONG PTR SessionHandler, ConnectionHandler, RequestHandler
DIM SHARED RequestMethod AS STRING, ResponseReceived AS LONG

FUNCTION IsValidMethod(CurrentMethod AS STRING) AS BYTE
  DIM ArrayMethods(HTTP_Methods.Count - 1) AS STRING = { _
    "HEAD", "GET", "POST", "PUT", "DELETE" _
  }
  FOR i AS ULONG = 0 TO HTTP_Methods.Count - 1
    IF UCASE(CurrentMethod) = ArrayMethods(i) THEN
      IsValidMethod = -1
      EXIT FOR
    END IF
  NEXT
END FUNCTION


FUNCTION SplitUrl(FullUrl AS STRING, UrlPart AS URL_Properties) AS STRING
  IF FullUrl <> "" AND UrlPart < URL_Properties.Count THEN
    DIM Result AS STRING
    DIM AS ULONG ColonPos, DoubleSlashPos, PathStartPos
    ColonPos = INSTR(FullUrl, ":")
    DoubleSlashPos = INSTR(FullUrl, "//")
    SELECT CASE UrlPart
      CASE URL_Protocol
        Result = MID(FullUrl, 1, ColonPos - 1)
      CASE URL_HostName
        PathStartPos = INSTR(DoubleSlashPos + 2, FullUrl, "/")
        Result = MID(FullUrl, DoubleSlashPos + 2, PathStartPos - DoubleSlashPos - 2)
      CASE URL_Port
        ColonPos = INSTR(ColonPos + 1, FullUrl, ":")
        IF ColonPos THEN
          'Another colon found?, must be the port number
          PathStartPos = INSTR(ColonPos, FullUrl, "/")
          Result = MID(FullUrl, ColonPos + 1, PathStartPos - ColonPos - 1)
        END IF
      CASE URL_URI
        PathStartPos = INSTR(DoubleSlashPos + 2, FullUrl, "/")
        Result = MID(FullUrl, PathStartPos)
      'CASE URL_QueryString
      'CASE URL_Fragment
    END SELECT
    SplitUrl = Result
  END IF
END FUNCTION

SUB HTTP_Open(Method AS STRING, Url AS STRING)
  DIM AS ULONG ServerPort, RequestFlags
  DIM AS STRING ServerProtocol
  IF Method <> "" AND IsValidMethod(Method) THEN
    RequestMethod = UCASE(Method)
  ELSE
    RequestMethod = "GET"
  END IF
  SessionHandler = WinHttpOpen( _
    WSTR(IguanaUserAgent), _
    WINHTTP_ACCESS_TYPE_NO_PROXY, _
    WINHTTP_NO_PROXY_NAME, _
    WINHTTP_NO_PROXY_BYPASS _
  )
  IF SessionHandler THEN
    ServerProtocol = SplitUrl(Url, URL_Protocol)
    ServerPort = VALINT(SplitUrl(Url, URL_Port))
    IF NOT ServerPort THEN
      'Port empty, use default ones
      SELECT CASE ServerProtocol
        CASE "http"
          ServerPort = INTERNET_DEFAULT_HTTP_PORT
        CASE "https"
          ServerPort = INTERNET_DEFAULT_HTTPS_PORT
      END SELECT
    END IF
    ConnectionHandler = WinHttpConnect( _
      SessionHandler, _
      WSTR(SplitUrl(Url, URL_HostName)), _
      ServerPort _
    )
    IF ConnectionHandler THEN
      SELECT CASE ServerProtocol
        CASE "http"
          RequestFlags = WINHTTP_FLAG_REFRESH
        CASE "https"
          RequestFlags = WINHTTP_FLAG_REFRESH OR WINHTTP_FLAG_SECURE
      END SELECT
      RequestHandler = WinHttpOpenRequest( _
        ConnectionHandler, _
        WSTR(Method), _
        WSTR(SplitUrl(Url, URL_URI)), _
        0, _
        WINHTTP_NO_REFERER, _
        WINHTTP_DEFAULT_ACCEPT_TYPES, _
        RequestFlags _
      )
    END IF
  END IF
END SUB

SUB HTTP_Send(RequestBody AS STRING = "")
  IF RequestHandler THEN
    DIM RequestFlags AS ULONG, ReTry AS LONG, RequestLen AS ULONG, RequestData AS WSTRING PTR
    IF RequestBody <> "" AND RequestMethod = "POST" OR RequestMethod = "PUT" THEN
      RequestData[0] = WSTR(RequestBody)
      RequestLen = LEN(RequestData)
    ELSE
      RequestData = WINHTTP_NO_REQUEST_DATA
    END IF
    DO
      ReTry = 0
      IF WinHttpSendRequest( _
        RequestHandler, _
        WINHTTP_NO_ADDITIONAL_HEADERS, _
        0, _
        VarPtr(RequestData), _
        RequestLen, _
        RequestLen, _
        0 _
      ) THEN
        ResponseReceived = WinHttpReceiveResponse(RequestHandler)
      ELSE
        LastError = GetLastError()
        IF LastError THEN
          SELECT CASE LastError
            CASE ERROR_WINHTTP_SECURE_INVALID_CA
              RequestFlags = _
                SECURITY_FLAG_IGNORE_UNKNOWN_CA OR _
                SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE OR _
                SECURITY_FLAG_IGNORE_CERT_CN_INVALID OR _
                SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
              IF WinHttpSetOption( _
                RequestHandler, _
                WINHTTP_OPTION_SECURITY_FLAGS, _
                VarPtr(RequestFlags), _
                LEN(RequestFlags) _
              ) THEN
                ReTry = -1
              END IF
            CASE ELSE
              ReTry = 0
          END SELECT
        END IF
      END IF
    LOOP WHILE ReTry
  END IF
END SUB

FUNCTION HTTP_GetResponse() AS STRING
  IF ResponseReceived THEN
    DIM ResponseLen() AS UBYTE, ResponseText AS STRING, SavedLen AS ULONG, CharsLeft AS ULONG
    DO
      CharsLeft = 0
      IF WinHttpQueryDataAvailable(RequestHandler, VarPtr(CharsLeft)) THEN
        REDIM ResponseLen(CharsLeft + 1)
        IF WinHttpReadData(RequestHandler, VarPtr(ResponseLen(0)), CharsLeft, VarPtr(SavedLen)) THEN
          FOR i AS ULONG = 0 TO UBOUND(ResponseLen)
            ResponseText += CHR(ResponseLen(i))
          NEXT
        END IF
      END IF
    LOOP UNTIL CharsLeft = 0
    HTTP_GetResponse = ResponseText
  END IF
END FUNCTION

SUB HTTP_Clean()
  WinHttpCloseHandle(RequestHandler)
  WinHttpCloseHandle(ConnectionHandler)
  WinHttpCloseHandle(SessionHandler)
END SUB