diff --git a/containers.v b/containers.v index 7b0af3a..14d295f 100644 --- a/containers.v +++ b/containers.v @@ -1,7 +1,7 @@ module docker import time -import net.http { Method } +import net.http import types { ContainerListItem } [params] @@ -13,7 +13,7 @@ pub struct ContainerListConfig { } pub fn (mut d DockerConn) container_list(c ContainerListConfig) ![]ContainerListItem { - d.get('/containers/json') + d.request(.get, '/containers/json', {}) d.params(c) d.send()! @@ -36,14 +36,17 @@ pub: } pub fn (mut d DockerConn) container_create(c NewContainer) !CreatedContainer { - d.send_request_with_json(Method.post, '/containers/create', c)! + d.request(.post, '/containers/create', {}) + d.body_json(c) + d.send()! return d.read_json_response() } // start_container starts the container with the given id. pub fn (mut d DockerConn) container_start(id string) ! { - d.send_request(Method.post, '/containers/$id/start')! + d.request(.post, '/containers/$id/start', {}) + d.send()! d.read_response()! } @@ -66,7 +69,8 @@ pub mut: } pub fn (mut d DockerConn) container_inspect(id string) !ContainerInspect { - d.send_request(Method.get, '/containers/$id/json')! + d.request(.get, '/containers/$id/json', {}) + d.send()! mut data := d.read_json_response()! @@ -81,12 +85,17 @@ pub fn (mut d DockerConn) container_inspect(id string) !ContainerInspect { } pub fn (mut d DockerConn) container_remove(id string) ! { - d.send_request(Method.delete, '/containers/$id')! + d.request(.delete, '/containers/$id', {}) + d.send()! d.read_response()! } pub fn (mut d DockerConn) container_get_logs(id string) !&StreamFormatReader { - d.send_request(Method.get, '/containers/$id/logs?stdout=true&stderr=true')! + d.request(.get, '/containers/$id/logs', { + 'stdout': 'true' + 'stderr': 'true' + }) + d.send()! d.read_response_head()! d.check_error()! diff --git a/docker.v b/docker.v index 8e6fdac..093e657 100644 --- a/docker.v +++ b/docker.v @@ -4,7 +4,6 @@ import net.unix import io import net.http import strings -import net.urllib import json import util @@ -27,8 +26,11 @@ mut: url string params map[string]string content_type string - head http.Response - body string + // Before send: body of the request + // After send: body of response + body string + // HTTP head of the response + head http.Response } // new_conn creates a new connection to the Docker daemon. @@ -48,36 +50,6 @@ pub fn (mut d DockerConn) close() ! { d.socket.close()! } -// send_request sends an HTTP request without body. -fn (mut d DockerConn) send_request(method http.Method, url_str string) ! { - url := urllib.parse('/$docker.api_version$url_str')! - req := '$method $url.request_uri() HTTP/1.1\nHost: localhost\n\n' - - d.socket.write_string(req)! - - // When starting a new request, the reader needs to be reset. - d.reader = io.new_buffered_reader(reader: d.socket) -} - -// send_request_with_body sends an HTTP request with the given body. -fn (mut d DockerConn) send_request_with_body(method http.Method, url_str string, content_type string, body string) ! { - url := urllib.parse('/$docker.api_version$url_str')! - req := '$method $url.request_uri() HTTP/1.1\nHost: localhost\nContent-Type: $content_type\nContent-Length: $body.len\n\n$body\n\n' - - d.socket.write_string(req)! - - // When starting a new request, the reader needs to be reset. - d.reader = io.new_buffered_reader(reader: d.socket) -} - -// send_request_with_json is a convenience wrapper around -// send_request_with_body that encodes the input as JSON. -fn (mut d DockerConn) send_request_with_json(method http.Method, url_str string, data &T) ! { - body := json.encode(data) - - return d.send_request_with_body(method, url_str, 'application/json', body) -} - // read_response_head consumes the socket's contents until it encounters // '\r\n\r\n', after which it parses the response as an HTTP response. // Importantly, this function never consumes the reader past the HTTP @@ -91,34 +63,31 @@ fn (mut d DockerConn) read_response_head() ! { } fn (mut d DockerConn) read_response_body() ! { - if d.head.status() == .no_content { - return - } - - if d.head.header.get(http.CommonHeader.transfer_encoding) or { '' } == 'chunked' { - mut builder := strings.new_builder(1024) - mut body := d.get_chunked_response_reader() - - util.reader_to_writer(mut body, mut builder)! - d.body = builder.str() - - return - } - - content_length := d.head.header.get(.content_length)!.int() - - if content_length == 0 { + if d.head.status() == .no_content { return } - mut buf := []u8{len: docker.buf_len} - mut c := 0 mut builder := strings.new_builder(docker.buf_len) - for builder.len < content_length { - c = d.reader.read(mut buf)! + if d.head.header.get(.transfer_encoding) or { '' } == 'chunked' { + mut body_stream := d.get_chunked_response_reader() - builder.write(buf[..c])! + util.reader_to_writer(mut body_stream, mut builder)! + } else { + content_length := d.head.header.get(.content_length)!.int() + + if content_length == 0 { + return + } + + mut buf := []u8{len: docker.buf_len} + mut c := 0 + + for builder.len < content_length { + c = d.reader.read(mut buf)! + + builder.write(buf[..c])! + } } d.body = builder.str() @@ -131,7 +100,7 @@ fn (mut d DockerConn) read_response_body() ! { fn (mut d DockerConn) read_response() ! { d.read_response_head()! d.check_error()! - d.read_response_body()! + d.read_response_body()! } // read_json_response is a convenience function that runs read_response diff --git a/images.v b/images.v index 03f9387..d879cd9 100644 --- a/images.v +++ b/images.v @@ -1,10 +1,11 @@ module docker -import net.http { Method } +import net.http import types { Image } pub fn (mut d DockerConn) image_inspect(image string) !Image { - d.send_request(.get, '/images/$image/json')! + d.request(.get, '/images/$image/json', {}) + d.send()! data := d.read_json_response()! @@ -13,7 +14,11 @@ pub fn (mut d DockerConn) image_inspect(image string) !Image { // pull_image pulls the given image:tag. pub fn (mut d DockerConn) pull_image(image string, tag string) ! { - d.send_request(Method.post, '/images/create?fromImage=$image&tag=$tag')! + d.request(.post, '/images/create', { + 'fromImage': image + 'tag': tag + }) + d.send()! d.read_response_head()! d.check_error()! @@ -29,14 +34,19 @@ pub fn (mut d DockerConn) pull_image(image string, tag string) ! { // create_image_from_container creates a new image from a container. pub fn (mut d DockerConn) create_image_from_container(id string, repo string, tag string) !Image { - d.send_request(.post, '/commit?container=$id&repo=$repo&tag=$tag')! - data := d.read_json_response()! + d.request(.post, '/commit', { + 'container': id + 'repo': repo + 'tag': tag + }) + d.send()! - return data + return d.read_json_response()! } // remove_image removes the image with the given id. pub fn (mut d DockerConn) remove_image(id string) ! { - d.send_request(.delete, '/images/$id')! + d.request(.delete, '/images/$id', {}) + d.send()! d.read_response()! } diff --git a/request.v b/request.v index fdee0d4..f844717 100644 --- a/request.v +++ b/request.v @@ -3,17 +3,29 @@ module docker import net.http import net.urllib import io +import json -fn (mut d DockerConn) request(method http.Method, url_str string) { +fn (mut d DockerConn) request(method http.Method, url string, params map[string]string) { d.method = method - d.url = url_str - d.params.clear() + d.url = url d.content_type = '' d.body = '' + + d.params.clear() + + for key, value in params { + d.params[key] = urllib.query_escape(value.replace("'", '"')) + } } -fn (mut d DockerConn) get(url_str string) { - d.request(http.Method.get, url_str) +fn (mut d DockerConn) body(content_type string, body string) { + d.content_type = content_type + d.body = body +} + +fn (mut d DockerConn) body_json(data T) { + d.content_type = 'application/json' + d.body = json.encode(data) } fn (mut d DockerConn) params(o T) { diff --git a/volumes.v b/volumes.v index 5bd9936..6ce9905 100644 --- a/volumes.v +++ b/volumes.v @@ -1,6 +1,6 @@ module docker -import net.http { Method } +import net.http import time struct UsageData { @@ -36,7 +36,8 @@ struct VolumeListResponse { } pub fn (mut d DockerConn) volume_list() !VolumeListResponse { - d.send_request(Method.get, '/volumes')! + d.request(.get, '/volumes', {}) + d.send()! mut data := d.read_json_response()!