-
-
Save sciolist/2e741ff651ffe58b28f4 to your computer and use it in GitHub Desktop.
import GCDWebServer | |
let webserver = GCDWebServer() | |
let proxy = CorsProxy(webserver: webserver!, urlPrefix: "/CORS/") | |
webserver.startWithPort(8080, bonjourName: nil) | |
// $ curl -i -H "Origin: test-site" http://127.0.0.1/CORS/http://my/test/site | |
// HTTP/1.1 200 OK | |
// Access-Control-Allow-Origin: test-site | |
// Server: GCDWebServer | |
// Access-Control-Allow-Credentials: true | |
// Connection: Close | |
// Access-Control-Allow-Methods: PUT,POST,GET,PATCH,DELETE | |
// Cache-Control: no-cache |
import GCDWebServer | |
class CorsProxy { | |
init(webserver : GCDWebServer!, urlPrefix: String) { | |
var prefix = | |
(urlPrefix.hasPrefix("/") ? "" : "/") | |
+ urlPrefix | |
+ (urlPrefix.hasSuffix("/") ? "" : "/") | |
let pattern = "^" + NSRegularExpression.escapedPatternForString(prefix) + ".*" | |
webserver.addHandlerForMethod("POST", pathRegex: pattern, requestClass: GCDWebServerDataRequest.self, processBlock:{ req in | |
return self.sendProxyResult(prefix, req: req) | |
}) | |
webserver.addHandlerForMethod("PUT", pathRegex: pattern, requestClass: GCDWebServerDataRequest.self, processBlock:{ req in | |
return self.sendProxyResult(prefix, req: req) | |
}) | |
webserver.addHandlerForMethod("PATCH", pathRegex: pattern, requestClass: GCDWebServerDataRequest.self, processBlock:{ req in | |
return self.sendProxyResult(prefix, req: req) | |
}) | |
webserver.addHandlerForMethod("DELETE", pathRegex: pattern, requestClass: GCDWebServerDataRequest.self, processBlock:{ req in | |
return self.sendProxyResult(prefix, req: req) | |
}) | |
webserver.addHandlerForMethod("GET", pathRegex: pattern, requestClass: GCDWebServerRequest.self, processBlock:{ req in | |
return self.sendProxyResult(prefix, req: req) | |
}) | |
webserver.addHandlerForMethod("OPTIONS", pathRegex: pattern, requestClass: GCDWebServerRequest.self, processBlock:{ req in | |
return self.sendCorsHeaders(prefix, req: req) | |
}) | |
} | |
func sendProxyResult(prefix: String, req: GCDWebServerRequest) -> GCDWebServerResponse! { | |
let query = req.URL.query == nil ? "" : "?" + req.URL.query! | |
var url = NSURL(string: req.path.substringFromIndex(prefix.endIndex) + query) | |
if (url == nil) { return sendError("Invalid URL") } | |
var err: NSErrorPointer = nil | |
var urlResp: NSURLResponse? | |
let urlReq = NSMutableURLRequest(URL: url!, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData, timeoutInterval: 320000) | |
urlReq.HTTPMethod = req.method | |
urlReq.allHTTPHeaderFields = req.headers | |
urlReq.allHTTPHeaderFields?["Host"] = url!.host | |
if (req.hasBody()) { | |
urlReq.HTTPBody = (req as! GCDWebServerDataRequest).data | |
} | |
let output = NSURLConnection.sendSynchronousRequest(urlReq | |
, returningResponse: &urlResp | |
, error: err) | |
if (urlResp == nil) { | |
return sendError(output == nil ? nil : NSString(data: output!, encoding: NSUTF8StringEncoding) as? String); | |
} | |
var httpResponse = urlResp as! NSHTTPURLResponse | |
let resp = GCDWebServerDataResponse(data: output == nil ? NSData() : output, contentType: "application/x-unknown") | |
resp.statusCode = httpResponse.statusCode | |
for key in httpResponse.allHeaderFields { | |
if (toString(key.0) == "Content-Encoding") { continue; } | |
resp.setValue(toString(key.1), forAdditionalHeader: toString(key.0)) | |
} | |
if (output != nil) { | |
resp.setValue(String(output!.length), forAdditionalHeader: "Content-Length") | |
} | |
return resp | |
} | |
func sendCorsHeaders(prefix: String, req: GCDWebServerRequest) -> GCDWebServerResponse! { | |
let resp = GCDWebServerResponse() | |
resp.setValue(toString(req.headers["Origin"]), forAdditionalHeader: "Access-Control-Allow-Origin") | |
resp.setValue("PUT,POST,GET,PATCH,DELETE", forAdditionalHeader: "Access-Control-Allow-Methods") | |
resp.setValue("true", forAdditionalHeader: "Access-Control-Allow-Credentials") | |
return resp | |
} | |
func sendError(error: String?) -> GCDWebServerResponse! { | |
var msg = error == nil ? "An error occured" : error! | |
let errorData = msg.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true) | |
let resp = GCDWebServerDataResponse(data: errorData, contentType: "text/plain") | |
resp.statusCode = 500 | |
return resp | |
} | |
func toString(v: AnyObject?) -> String! { | |
if (v == nil) { return ""; } | |
return String(stringInterpolationSegment: v!) | |
} | |
} |
@anoop4real Here's a Swift 4 (not Swift 4.2, sadly) direct translation of the main part (no improvements like migrating sync methods to async ones; and a couple of warnings about deprecated methods) that I've just briefly tested for GET a request to https://www.bbc.co.uk. It appears to work!
import Foundation
import GCDWebServer
/* https://gist.github.com/zqqf16/fc1b2f37eead98ec44b5bc682b07796b https://gist.github.com/sciolist/2e741ff651ffe58b28f4 */
class CorsProxy {
var webServer: GCDWebServer
init(webserver: GCDWebServer, urlPrefix: String) {
self.webServer = webserver
let prefix =
(urlPrefix.hasPrefix("/") ? "" : "/")
+ urlPrefix
+ (urlPrefix.hasSuffix("/") ? "" : "/")
let pattern = "^" + NSRegularExpression.escapedPattern(for: prefix) + ".*"
webserver.addHandler(forMethod: "POST", pathRegex: pattern, request: GCDWebServerDataRequest.self, processBlock:{ req in
return self.sendProxyResult(prefix, req: req)
})
webserver.addHandler(forMethod: "PUT", pathRegex: pattern, request: GCDWebServerDataRequest.self, processBlock:{ req in
return self.sendProxyResult(prefix, req: req)
})
webserver.addHandler(forMethod: "PATCH", pathRegex: pattern, request: GCDWebServerDataRequest.self, processBlock:{ req in
return self.sendProxyResult(prefix, req: req)
})
webserver.addHandler(forMethod: "DELETE", pathRegex: pattern, request: GCDWebServerDataRequest.self, processBlock:{ req in
return self.sendProxyResult(prefix, req: req)
})
webserver.addHandler(forMethod: "GET", pathRegex: pattern, request: GCDWebServerRequest.self, processBlock:{ req in
return self.sendProxyResult(prefix, req: req)
})
webserver.addHandler(forMethod: "OPTIONS", pathRegex: pattern, request: GCDWebServerRequest.self, processBlock:{ req in
return self.sendCorsHeaders(prefix, req: req)
})
}
func sendProxyResult(_ prefix: String, req: GCDWebServerRequest) -> GCDWebServerResponse? {
let query = req.url.query == nil ? "" : "?" + req.url.query!
let url = URL(string: req.path.substring(from: prefix.endIndex) + query)
if (url == nil) { return sendError(error: "Invalid URL") }
var urlResp: URLResponse?
var urlReq = URLRequest(url: url!, cachePolicy: NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData, timeoutInterval: 320000)
urlReq.httpMethod = req.method
urlReq.allHTTPHeaderFields = req.headers as? [String : String]
urlReq.allHTTPHeaderFields?["Host"] = url!.host
if (req.hasBody()) {
urlReq.httpBody = (req as! GCDWebServerDataRequest).data
}
do {
let output = try NSURLConnection.sendSynchronousRequest(urlReq, returning: &urlResp)
if (urlResp == nil) {
return sendError(error: String(data: output, encoding: String.Encoding.utf8));
}
let httpResponse = urlResp as! HTTPURLResponse
let resp = GCDWebServerDataResponse(data: output, contentType: "application/x-unknown")
resp.statusCode = httpResponse.statusCode
for key in httpResponse.allHeaderFields {
if (toString(v: key.0 as AnyObject) == "Content-Encoding") { continue; }
resp.setValue(toString(v: key.1 as AnyObject), forAdditionalHeader: toString(v: key.0 as AnyObject))
}
resp.setValue(String(output.count), forAdditionalHeader: "Content-Length")
return resp
} catch {
print("Proxy error: ", error.localizedDescription)
return nil
}
}
func sendCorsHeaders(_ prefix: String, req: GCDWebServerRequest) -> GCDWebServerResponse! {
let resp = GCDWebServerResponse()
resp.setValue(toString(v: req.headers["Origin"] as AnyObject), forAdditionalHeader: "Access-Control-Allow-Origin")
resp.setValue("PUT,POST,GET,PATCH,DELETE", forAdditionalHeader: "Access-Control-Allow-Methods")
resp.setValue("true", forAdditionalHeader: "Access-Control-Allow-Credentials")
return resp
}
func sendError(error: String?) -> GCDWebServerResponse! {
let msg = error == nil ? "An error occured" : error!
let errorData = msg.data(using: String.Encoding.utf8, allowLossyConversion: true)
let resp = GCDWebServerDataResponse(data: errorData!, contentType: "text/plain")
resp.statusCode = 500
return resp
}
func toString(v: AnyObject?) -> String! {
if (v == nil) { return ""; }
return String(stringInterpolationSegment: v!)
}
}
Hello @shirakaba and @sciolist
Why do you have so big timeintervals here timeoutInterval: 320000
?
Isn't 88 hours timeout too big?
Thanks!
@troyanskiy I'm afraid I can't really comment on any of the implementation/design decisions (and I don't pretend to be an expert on proxying), as I merely translated the code to Swift 4.
I would however agree that 88 hours does seem rather excessive for a network timeout; by contrast, web browsers frequently use around 40 seconds for request timeouts.
@troyanskiy 320000 seconds is indeed far too long for any real use case, there's no good reason for it to be that high.
Swift 3 version for this?