# HG changeset patch # User Mike Pavone # Date 1376172416 25200 # Node ID 55e0dca7d3d7eabcf1533e611a76b57b17ce7f65 # Parent d6e79885bd3b94622249d1cbb7448e40ecbad617 Partial implementation of HTTP get requests diff -r d6e79885bd3b -r 55e0dca7d3d7 modules/http.tp --- a/modules/http.tp Sat Aug 10 14:50:38 2013 -0700 +++ b/modules/http.tp Sat Aug 10 15:06:56 2013 -0700 @@ -1,41 +1,83 @@ -#{ - client:usingPort <- :address :port{ +{ + response <- :_headers _status _sock _data { + _open? <- true + _body <- "" + _length <- int32: (_headers get: "Content-Length" withDefault: "-1") + _chunked? <- (_headers get: "Transfer-Encoding" withDefault: "") = "chunked" + _code <- int32: _status #{ - get <- :path { - sock <- socket connectTo: address onPort: port - sock send: "GET " . path . " HTTP/1.1\r\nHost: " . address . "\r\n\r\n" - resp <- "" - waiting <- true - headerText <- "" - rest <- "" - while: { waiting } do: { - data <- sock recv 4096 - resp <- resp . data - pos <- resp find: "\r\n\r\n" else: { -1 } - if: pos >= 0 { - waiting <- false - headerText <- resp from: 0 withLength: pos - rest <- resp from: pos + 4 + headers <- { _headers } + status <- { _status } + statusCode <- { _code } + body <- { + if: _open? { + if: _chunked? { + + } else: { + if: _length >= 0 { + _body <- _data . (_sock recvAll: (_length - (_data byte_length))) + } else: { + chunk <- "" + while: { + chunk <- _sock recv: 4096 + (chunk length) > 0 + } do: { + _data <- _data . chunk + } + _body <- _data + } } + _data <- "" + close } - print: "Headers:\n" . headerText . "\n" - print: "Rest:\n" . rest . "\n" - _headers <- (headerText split: "\r\n") fold: (dict linear) with: :acc curLine{ - //TODO: support multiple headers with the same name - part <- curLine partitionOn: ":" - acc set: (part before) (trim: (part after)) + _body + } + close <- { + if: _open? { + _sock close + _open? <- false } - _closed <- false - #{ - - } - sock close - rest } } } + #{ + client:usingPort <- :address :port{ + #{ + get <- :path { + sock <- socket connectTo: address onPort: port + sock send: "GET " . path . " HTTP/1.1\r\nHost: " . address . "\r\n\r\n" + resp <- "" + waiting <- true + headerText <- "" + rest <- "" + status <- "" + while: { waiting } do: { + data <- sock recv 4096 + resp <- resp . data + pos <- resp find: "\r\n\r\n" else: { -1 } + if: pos >= 0 { + waiting <- false + statusEnd <- resp find: "\r\n" else: { 0 } + statusStart <- (resp find: " " else: { 0 }) + 1 + status <- resp from: statusStart withLength: (statusEnd - statusStart) + headerText <- resp from: statusEnd + 2 withLength: pos - (statusEnd + 2) + rest <- resp from: pos + 4 + } + } + headers <- (headerText splitOn: "\r\n") fold: (dict linear) with: :acc curLine{ + //TODO: support multiple headers with the same name + part <- curLine partitionOn: ":" + acc set: (trim: (part before)) (trim: (part after)) + } - client <- :address { - client: address usingPort: 80 + + response: headers status sock rest + } + } + } + + client <- :address { + client: address usingPort: 80 + } } } diff -r d6e79885bd3b -r 55e0dca7d3d7 modules/string.tp --- a/modules/string.tp Sat Aug 10 14:50:38 2013 -0700 +++ b/modules/string.tp Sat Aug 10 15:06:56 2013 -0700 @@ -187,6 +187,34 @@ pieces append: self } + trim <- { + l <- length + start <- 0 + space <- " " byte: 0 + tab <- "\t" byte: 0 + nl <- "\n" byte: 0 + cr <- "\r" byte: 0 + + while: { + if: start < l { + b <- byte: start + b = space || b = tab || b = nl || b = cr + } + } do: { + start <- start + 1 + } + end <- l + while: { + if: end > 0 { + b <- byte: end + b = space || b = tab || b = nl || b = cr + } + } do: { + end <- end + 1 + } + from: start withLength: (end - start) + } + isInteger? <- { false } isString? <- { true } } diff -r d6e79885bd3b -r 55e0dca7d3d7 samples/http.tp --- a/samples/http.tp Sat Aug 10 14:50:38 2013 -0700 +++ b/samples/http.tp Sat Aug 10 15:06:56 2013 -0700 @@ -5,6 +5,13 @@ server <- args get: 1 } cli <- http client: server - print: (string: (cli get: "/")) + resp <- (cli get: "/") + print: "Status: " . (resp status) . "\n" + print: "Code: " . (resp statusCode) . "\n" + print: "Headers:\n" + foreach: (resp headers) :key val { + print: key . " -> " . val . "\n" + } + print: "Body:\n" . (resp body) . "\n" } }