diff options
author | Dominik Picheta <dominikpicheta@googlemail.com> | 2012-06-09 14:24:41 +0100 |
---|---|---|
committer | Dominik Picheta <dominikpicheta@googlemail.com> | 2012-06-09 14:24:41 +0100 |
commit | 7364d725482cf27794fd42b13751348cec5d1a1e (patch) | |
tree | 223e7c1bff9247cdd1f54669ee0320d445d3bdb9 /lib/pure/httpclient.nim | |
parent | ce933c90a48ddf0331016edbc684ba6937412e22 (diff) | |
download | Nim-7364d725482cf27794fd42b13751348cec5d1a1e.tar.gz |
Fixed httpclient bugs, fixed socket bugs and fixed sockets for windows.
Diffstat (limited to 'lib/pure/httpclient.nim')
-rwxr-xr-x | lib/pure/httpclient.nim | 140 |
1 files changed, 71 insertions, 69 deletions
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index c4dbd8509..184bca867 100755 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -87,10 +87,11 @@ proc charAt(d: var string, i: var int, s: TSocket): char {.inline.} = i = 0 result = d[i] -proc parseChunks(d: var string, start: int, s: TSocket): string = +proc parseChunks(s: TSocket): string = # get chunks: - var i = start + var i = 0 result = "" + var d = s.recv().string while true: var chunkSize = 0 var digitFound = false @@ -136,88 +137,87 @@ proc parseChunks(d: var string, start: int, s: TSocket): string = # skip trailing CR-LF: while charAt(d, i, s) in {'\C', '\L'}: inc(i) -proc parseBody(d: var string, start: int, s: TSocket, +proc parseBody(s: TSocket, headers: PStringTable): string = + result = "" if headers["Transfer-Encoding"] == "chunked": - result = parseChunks(d, start, s) + result = parseChunks(s) else: - result = substr(d, start) # -REGION- Content-Length # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.3 var contentLengthHeader = headers["Content-Length"] if contentLengthHeader != "": var length = contentLengthHeader.parseint() - while result.len() < length: result.add(s.recv.string) + result = newString(length) + var received = 0 + while true: + if received >= length: break + let r = s.recv(addr(result[received]), length-received) + if r == 0: break + received += r + if received != length: + httpError("Got invalid content length. Expected: " & $length & + " got: " & $received) else: # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.4 TODO # -REGION- Connection: Close # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.5 if headers["Connection"] == "close": + var buf = "" while True: - var moreData = recv(s).string - if moreData.len == 0: break - result.add(moreData) + buf = newString(4000) + let r = s.recv(addr(buf[0]), 4000) + if r == 0: break + buf.setLen(r) + result.add(buf) -proc parseResponse(s: TSocket): TResponse = - var d = s.recv.string # Warning: without a Connection: Close header this will not work. - var i = 0 - - # Parse the version - # Parses the first line of the headers - # ``HTTP/1.1`` 200 OK - var L = skipIgnoreCase(d, "HTTP/1.1", i) - if L > 0: - result.version = "1.1" - inc(i, L) - else: - L = skipIgnoreCase(d, "HTTP/1.0", i) - if L > 0: - result.version = "1.0" - inc(i, L) - else: - httpError("invalid HTTP header") - L = skipWhiteSpace(d, i) - if L <= 0: httpError("invalid HTTP header") - inc(i, L) - - result.status = "" - while d[i] notin {'\C', '\L', '\0'}: - result.status.add(d[i]) - inc(i) - if d[i] == '\C': inc(i) - if d[i] == '\L': inc(i) - else: httpError("invalid HTTP header, CR-LF expected") - - # Parse the headers - # Everything after the first line leading up to the body - # htype: hvalue +proc parseResponse(s: TSocket, getBody: bool): TResponse = + var parsedStatus = false + var linei = 0 + var fullyRead = false + var line = "" result.headers = newStringTable(modeCaseInsensitive) - while true: - var key = "" - while d[i] != ':': - if d[i] == '\0': httpError("invalid HTTP header, ':' expected") - key.add(d[i]) - inc(i) - inc(i) # skip ':' - if d[i] == ' ': inc(i) # skip if the character is a space - var val = "" - while d[i] notin {'\C', '\L', '\0'}: - val.add(d[i]) - inc(i) - - result.headers[key] = val - - if d[i] == '\C': inc(i) - if d[i] == '\L': inc(i) - else: httpError("invalid HTTP header, CR-LF expected") - - if d[i] == '\C': inc(i) - if d[i] == '\L': - inc(i) - break - - result.body = parseBody(d, i, s, result.headers) + while True: + line = "" + linei = 0 + if s.recvLine(line): + if line == "": break # We've been disconnected. + if line == "\c\L": + fullyRead = true + break + if not parsedStatus: + # Parse HTTP version info and status code. + var le = skipIgnoreCase(line, "HTTP/", linei) + if le <= 0: httpError("invalid http version") + inc(linei, le) + le = skipIgnoreCase(line, "1.1", linei) + if le > 0: result.version = "1.1" + else: + le = skipIgnoreCase(line, "1.0", linei) + if le <= 0: httpError("unsupported http version") + result.version = "1.0" + inc(linei, le) + # Status code + linei.inc skipWhitespace(line, linei) + result.status = line[linei .. -1] + parsedStatus = true + else: + # Parse headers + var name = "" + var le = parseUntil(line, name, ':', linei) + if le <= 0: httpError("invalid headers") + inc(linei, le) + if line[linei] != ':': httpError("invalid headers") + inc(linei) # Skip : + linei += skipWhitespace(line, linei) + + result.headers[name] = line[linei.. -1] + if not fullyRead: httpError("Connection was closed before full request has been made") + if getBody: + result.body = parseBody(s, result.headers) + else: + result.body = "" type THttpMethod* = enum ## the requested HttpMethod @@ -246,10 +246,10 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "", var headers = substr($httpMethod, len("http")) headers.add(" /" & r.path & r.query) + headers.add(" HTTP/1.1\c\L") add(headers, "Host: " & r.hostname & "\c\L") - add(headers, "Connection: Close\c\L") add(headers, extraHeaders) add(headers, "\c\L") @@ -258,6 +258,8 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "", if r.scheme == "https": when defined(ssl): s.wrapSocket(verifyMode = CVerifyNone) + else: + raise newException(EHttpRequestErr, "SSL support was not compiled in. Cannot connect over SSL.") port = TPort(443) if r.port != "": port = TPort(r.port.parseInt) @@ -266,7 +268,7 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "", if body != "": s.send(body) - result = parseResponse(s) + result = parseResponse(s, httpMethod != httpHEAD) s.close() proc redirection(status: string): bool = |