screenshot

This commit is contained in:
yanue 2019-06-03 18:28:37 +08:00
parent 469bae9370
commit 65914739f5
10 changed files with 325 additions and 10 deletions

View File

@ -14,6 +14,6 @@ target 'V2rayU' do
pod 'Preferences', :git => 'https://github.com/sindresorhus/Preferences.git'
pod 'Sparkle'
pod 'QRCoder'
pod "GCDWebServer"
pod "GCDWebServer", '~> 3.5.2'
end

View File

@ -1,8 +1,8 @@
PODS:
- Alamofire (4.7.3)
- GCDWebServer (3.4.2):
- GCDWebServer/Core (= 3.4.2)
- GCDWebServer/Core (3.4.2)
- GCDWebServer (3.5.2):
- GCDWebServer/Core (= 3.5.2)
- GCDWebServer/Core (3.5.2)
- Preferences (0.3.0)
- QRCoder (1.0.0)
- Sparkle (1.20.0)
@ -10,7 +10,7 @@ PODS:
DEPENDENCIES:
- Alamofire
- GCDWebServer
- GCDWebServer (~> 3.5.2)
- Preferences (from `https://github.com/sindresorhus/Preferences.git`)
- QRCoder
- Sparkle
@ -35,12 +35,12 @@ CHECKOUT OPTIONS:
SPEC CHECKSUMS:
Alamofire: c7287b6e5d7da964a70935e5db17046b7fde6568
GCDWebServer: 8d67ee9f634b4bb91eb4b8aee440318a5fc6debd
GCDWebServer: ead88cd14596dd4eae4f5830b8877c87c8728990
Preferences: 865793c3914c4de086c809a504a4157b8968cb90
QRCoder: 488f6869fb89ffcc3de3b4de2dabd200a6573f3c
Sparkle: 48999e7ee032f05ca05e28451eadf4af8ede6b44
SwiftyJSON: c4bcba26dd9ec7a027fc8eade48e2c911f229e96
PODFILE CHECKSUM: efe1d5761f445cd3b9cf0d742a67c364a492fc56
PODFILE CHECKSUM: 58098d0825211da73cdc15b835005b970bed1875
COCOAPODS: 1.6.1

View File

@ -41,6 +41,7 @@
6D6DF5F67A917F78FF652D45 /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = 6D6DF8F5112A8407EE959998 /* .gitignore */; };
6D6DF6553CF221AEB71913EE /* PreferenceGeneral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DFFB29020127E034AD168 /* PreferenceGeneral.swift */; };
6D6DF6F065067CD879201FF9 /* Scanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF87840CDA04AF0E9D36E /* Scanner.swift */; };
6D6DF70EA6ADC7628931E630 /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF1B2E4D0B61B7390F1B1 /* HttpServer.swift */; };
6D6DF807BE591BE396221EF3 /* PreferenceAdvance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF701C75D8D416096D717 /* PreferenceAdvance.swift */; };
6D6DF87497313615AFE3320A /* PreferencePac.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF3151BBA6E56836AF6DE /* PreferencePac.swift */; };
6D6DF8BFC33F97E9AFCA5A4B /* V2rayConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DFD9F5991B38B128888D6 /* V2rayConfig.swift */; };
@ -149,6 +150,7 @@
6D6DF085C3C411989C416D0D /* user-rule.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "user-rule.txt"; sourceTree = "<group>"; };
6D6DF12EA09D2D80788666D0 /* QrcodeWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QrcodeWindow.swift; sourceTree = "<group>"; };
6D6DF181C5D472DBBB71B72A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = V2rayU/Base.lproj/PreferencePac.xib; sourceTree = SOURCE_ROOT; };
6D6DF1B2E4D0B61B7390F1B1 /* HttpServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpServer.swift; sourceTree = "<group>"; };
6D6DF3151BBA6E56836AF6DE /* PreferencePac.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencePac.swift; sourceTree = "<group>"; };
6D6DF3A2857899778B719CE1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = V2rayU/Base.lproj/PreferenceAbout.xib; sourceTree = SOURCE_ROOT; };
6D6DF3C1564D0EBC5A99C9C1 /* gfwlist.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = gfwlist.txt; sourceTree = "<group>"; };
@ -293,6 +295,7 @@
667029D121AFB86E0079EF41 /* QrcodeWindow.xib */,
6D6DF12EA09D2D80788666D0 /* QrcodeWindow.swift */,
6D6DFBED570A0D1FDED0BD53 /* Preference */,
6D6DF1B2E4D0B61B7390F1B1 /* HttpServer.swift */,
);
path = V2rayU;
sourceTree = "<group>";
@ -631,6 +634,7 @@
6D6DF6553CF221AEB71913EE /* PreferenceGeneral.swift in Sources */,
66BC2B89228C589E00FBB716 /* V2raySubscript.swift in Sources */,
6D6DF807BE591BE396221EF3 /* PreferenceAdvance.swift in Sources */,
6D6DF70EA6ADC7628931E630 /* HttpServer.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -808,6 +812,7 @@
6646669521CBC0860094F0B7 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = RJYEH6TCJD;
@ -821,6 +826,7 @@
6646669621CBC0860094F0B7 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = RJYEH6TCJD;
@ -834,6 +840,7 @@
664EB37D216C9A5F00B6AE0D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
@ -897,6 +904,7 @@
664EB37E216C9A5F00B6AE0D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;

302
V2rayU/HttpServer.swift Normal file
View File

@ -0,0 +1,302 @@
//
// httpServer.swift
// HttpServer
//
// Created by Koksharov Alexandr on 01/12/2018.
// Copyright © 2018 Koksharov Alexandr. All rights reserved.
//
import Foundation
private let maxHeaderSize = 1024
enum HttpServerError: Error {
case description(String)
}
public protocol HttpRequest {
func getMethod() -> String
func getPath() -> String
func getVersion() -> String
func getHeader(name: String) -> String?
func getContent() throws -> String
}
public protocol HttpResponse {
func setHeader(name: String, value: String) -> Void
func setContent(body: String) -> Void
func setStatusCode(code: Int) -> Void
}
class HttpServer {
private let ip: String
private let port: UInt16
private let handler: (HttpRequest, HttpResponse) -> Void
// client sock
private var sock: Int32
private let queue = DispatchQueue(label: "HttpServer", qos: .userInitiated, attributes: .concurrent)
private var source: DispatchSourceRead? = nil
init(ip: String, port: UInt16, handler: @escaping (HttpRequest, HttpResponse) -> Void) {
self.ip = ip
self.port = port
self.handler = handler
self.sock = 0
}
func run() {
var addr = sockaddr_in(
sin_len: UInt8(MemoryLayout<sockaddr_in>.size),
sin_family: UInt8(AF_INET),
sin_port: port.bigEndian,
sin_addr: in_addr(s_addr: INADDR_ANY),
sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)
)
let listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
guard listenSock > -1 else {
print("listenSock \(errno.description)")
return
}
var reuseVal: Int32 = 1
setsockopt(listenSock, SOL_SOCKET, SO_REUSEPORT, &reuseVal, socklen_t(MemoryLayout<Int32>.size))
withUnsafePointer(to: &addr) {
let addrPtr = UnsafeRawPointer($0).assumingMemoryBound(to: sockaddr.self)
guard bind(listenSock, addrPtr, socklen_t(MemoryLayout<sockaddr_in>.size)) == 0 else {
print("withUnsafePointer \(errno.description)")
return
}
}
guard listen(listenSock, 3) == 0 else {
print("listen \(errno.description)")
return
}
self.source = DispatchSource.makeReadSource(fileDescriptor: listenSock, queue: self.queue)
if self.source == nil {
print("cant create dispatch source")
return
}
self.source!.setEventHandler {
var clientAddr = sockaddr_storage()
var clientAddrLen: socklen_t = socklen_t(MemoryLayout.size(ofValue: clientAddr))
let clientSock = withUnsafeMutablePointer(to: &clientAddr) {
accept(listenSock, UnsafeMutableRawPointer($0).assumingMemoryBound(to: sockaddr.self), &clientAddrLen)
}
guard clientSock > 0 else {
print("clientSock \(errno.description)")
return
}
var noSigPipe: Int32 = 1
setsockopt(clientSock, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, socklen_t(MemoryLayout<Int32>.size))
self.queue.async {
self.handleClient(socket: clientSock)
self.sock = clientSock
close(clientSock)
}
}
self.source!.resume()
}
func stop() {
if self.sock > 0 {
Darwin.shutdown(sock, SHUT_RDWR)
}
self.queue.async {
close(self.sock)
}
}
private func handleClient(socket sock: Int32) {
do {
let reader: HttpReader = try HttpReader(sock)
let writer: HttpWriter = HttpWriter(sock)
self.handler(reader, writer)
try writer.writeResponse()
} catch let e {
print("\(e)")
}
}
}
private class HttpWriter: HttpResponse {
private let socket: Int32
private let version: String = "HTTP/1.0"
private var statusCode: Int = 200
private var header = [String: String]()
private var body: String = ""
init(_ socket: Int32) {
self.socket = socket
}
func setHeader(name: String, value: String) {
self.header[name] = value
}
func setContent(body: String) {
self.body = body
}
func setStatusCode(code: Int) {
self.statusCode = code
}
private func getStatusLine() -> String {
return "\(version) \(statusCode) \(HTTPURLResponse.localizedString(forStatusCode: statusCode))\r\n"
}
private func writeText(text: String) throws {
try text.withCString { buf in
var written = 0
while written < text.lengthOfBytes(using: String.Encoding.utf8) {
let len = write(self.socket, buf + written, text.lengthOfBytes(using: String.Encoding.utf8) - written)
if len == -1 {
throw HttpServerError.description("\(errno.description)")
}
written = written + len
}
}
}
func writeResponse() throws {
try writeText(text: getStatusLine())
try header.forEach { (key, value) in
try writeText(text: "\(key): \(value)\r\n")
}
try writeText(text: "\r\n\(self.body)")
}
}
private class HttpReader: HttpRequest {
private let socket: Int32
private var isReadDone: Bool
private var method: String = "UNDEF"
private var path: String = "UNDEF"
private var version: String = "UNDEF"
private var header = [String: String]()
private var body: String = ""
private var bodyLength: Int = 0
init(_ sock: Int32) throws {
self.socket = sock
self.isReadDone = false
do {
var httpHeader: String = ""
var reqLine = ""
var reqFields: String = ""
repeat {
let new = try readText(maxLen: maxHeaderSize - httpHeader.lengthOfBytes(using: String.Encoding.utf8))
guard new.lengthOfBytes(using: String.Encoding.utf8) > 0 else {
throw HttpServerError.description("http header too short")
}
httpHeader.append(new)
if let headerRange = httpHeader.range(of: "\r\n\r\n") {
guard let reqLineRange = httpHeader.range(of: "\r\n") else {
throw HttpServerError.description("http requestline read error")
}
reqLine = String(httpHeader[..<reqLineRange.lowerBound])
reqFields = String(httpHeader[reqLineRange.upperBound..<headerRange.lowerBound])
self.body = String(httpHeader[headerRange.upperBound...])
} else {
if httpHeader.lengthOfBytes(using: String.Encoding.utf8) >= maxHeaderSize {
throw HttpServerError.description("http header too long")
}
}
} while reqLine == ""
let requestComponents = reqLine.components(separatedBy: " ")
guard requestComponents.count == 3 else {
throw HttpServerError.description("cant parse request line: '\(reqLine)'")
}
self.method = requestComponents[0]
self.path = requestComponents[1]
self.version = requestComponents[2]
reqFields.enumerateLines { (line, done) in
let field = line.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: true)
if field.count == 2 {
self.header[field[0].lowercased()] = field[1].trimmingCharacters(in: .whitespacesAndNewlines)
} else {
print("cant parse header line. '\(field)'")
}
}
if let lenStr = getHeader(name: "content-length"), let len = Int(lenStr) {
self.bodyLength = len
}
} catch let e {
throw e
}
}
func getMethod() -> String {
return self.method
}
func getPath() -> String {
return self.path
}
func getVersion() -> String {
return self.version
}
func getHeader(name: String) -> String? {
return self.header[name.lowercased()]
}
func getContentLength() -> Int {
return self.bodyLength
}
func getContent() throws -> String {
while self.body.lengthOfBytes(using: String.Encoding.utf8) < self.bodyLength {
do {
let new = try readText(maxLen: Int(self.bodyLength) - self.body.lengthOfBytes(using: String.Encoding.utf8))
self.body.append(new)
} catch let e {
throw e
}
}
if self.body.lengthOfBytes(using: String.Encoding.utf8) > self.bodyLength {
let i = self.body.index(self.body.startIndex, offsetBy: self.bodyLength)
self.body = String(self.body[..<i])
}
return self.body
}
private func readText(maxLen: Int) throws -> String {
let bytesPointer = UnsafeMutableRawPointer.allocate(byteCount: maxLen, alignment: 1)
let len = read(self.socket, bytesPointer, maxLen)
if (len == 0) {// client closed stream - EOF
throw HttpServerError.description("Connection closed")
}
if len < 0 {
throw HttpServerError.description(errno.description)
}
if let result = String.init(bytesNoCopy: bytesPointer, length: len, encoding: String.Encoding.utf8, freeWhenDone: true) {
return result
}
throw HttpServerError.description("Request interpretation error. Binary data?")
}
}

View File

@ -165,13 +165,18 @@ class V2rayLaunch: NSObject {
if webServer.isRunning {
webServer.stop()
}
_ = GeneratePACFile()
let pacPort = UserDefaults.get(forKey: .localPacPort) ?? "1085"
webServer.addGETHandler(forBasePath: "/", directoryPath: AppResourcesPath, indexFilename: nil, cacheAge: 3600, allowRangeRequests: true)
webServer.start(withPort: UInt(pacPort) ?? 1085, bonjourName: "GCD Web Server")
// webServer.start(options: <#T##[AnyHashable : Any]?#>)
// Start
try! webServer.start(options: [
"Port": UInt(pacPort) ?? 1085,
"BindToLocalhost": true
] );
}
}

BIN
screenshot/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 KiB

BIN
screenshot/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

BIN
screenshot/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

BIN
screenshot/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

BIN
screenshot/5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB