1// Copyright 2017 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import Foundation
16import Dispatch
17import KituraNet
18
19// fetch makes a synchronous request using KituraNet's ClientRequest class
20// https://github.com/IBM-Swift/Kitura-net/blob/master/Sources/KituraNet/ClientRequest.swift
21public func fetch(_ urlRequest: URLRequest) -> (Data?, HTTPURLResponse?, Error?) {
22  var data: Data?
23  var urlResponse: HTTPURLResponse?
24  let error: Error? = nil // make this mutable when we start using it
25  let sem = DispatchSemaphore(value: 0)
26  guard let method = urlRequest.httpMethod else {
27    return (data, urlResponse, error)
28  }
29  guard let url = urlRequest.url else {
30    return (data, urlResponse, error)
31  }
32  guard let scheme = url.scheme else {
33    return (data, urlResponse, error)
34  }
35  guard let host = url.host else {
36    return (data, urlResponse, error)
37  }
38  guard let port = url.port else {
39    return (data, urlResponse, error)
40  }
41  let options : [ClientRequest.Options] = [
42    .method(method),
43    .schema(scheme),
44    .hostname(host),
45    .port(Int16(port)),
46    .path(url.path),
47    // headers, etc
48  ]
49  let request = HTTP.request(options) { (response) in
50    guard let response = response else {
51      sem.signal()
52      return
53    }
54    var responseData = Data()
55    do {
56      let code = response.httpStatusCode
57      try response.readAllData(into: &responseData)
58      data = responseData
59      urlResponse = HTTPURLResponse(url:url,
60                                    statusCode:code.rawValue,
61                                    httpVersion:"HTTP/1.1",
62                                    headerFields:[:])
63      sem.signal()
64      return
65    } catch {
66      sem.signal()
67      return
68    }
69  }
70  if let requestData = urlRequest.httpBody {
71    request.write(from:requestData)
72  }
73  request.end() // send the request
74  // now wait on the semaphore for a response
75  let result = sem.wait(timeout: DispatchTime.distantFuture)
76  switch result {
77  case .success:
78    return (data, urlResponse, error)
79  default: // includes .timeout
80    return (data, urlResponse, error)
81  }
82}
83
84// fetch makes an asynchronous request using KituraNet's ClientRequest class
85// https://github.com/IBM-Swift/Kitura-net/blob/master/Sources/KituraNet/ClientRequest.swift
86public func fetch(_ urlRequest: URLRequest, callback:@escaping (Data?, HTTPURLResponse?, Error?) -> ()) {
87  var data: Data?
88  var urlResponse: HTTPURLResponse?
89  let error: Error? = nil // make this mutable when we start using it
90  guard let method = urlRequest.httpMethod else {
91    callback (data, urlResponse, error)
92	return
93  }
94  guard let url = urlRequest.url else {
95    callback (data, urlResponse, error)
96	return
97  }
98  guard let scheme = url.scheme else {
99    callback (data, urlResponse, error)
100	return
101  }
102  guard let host = url.host else {
103    callback (data, urlResponse, error)
104	return
105  }
106  guard let port = url.port else {
107    callback (data, urlResponse, error)
108	return
109  }
110  let options : [ClientRequest.Options] = [
111    .method(method),
112    .schema(scheme),
113    .hostname(host),
114    .port(Int16(port)),
115    .path(url.path),
116    // headers, etc
117  ]
118  let request = HTTP.request(options) { (response) in
119    guard let response = response else {
120      callback (data, urlResponse, nil)
121      return
122    }
123    var responseData = Data()
124    do {
125      let code = response.httpStatusCode
126      try response.readAllData(into: &responseData)
127      data = responseData
128      urlResponse = HTTPURLResponse(url:url,
129                                    statusCode:code.rawValue,
130                                    httpVersion:"HTTP/1.1",
131                                    headerFields:[:])
132      callback (data, urlResponse, nil)
133      return
134    } catch {
135      callback (data, urlResponse, nil)
136      return
137    }
138  }
139  if let requestData = urlRequest.httpBody {
140    request.write(from:requestData)
141  }
142  request.end() // send the request
143}
144