mirror of
https://github.com/yanue/V2rayU.git
synced 2025-06-27 05:30:09 +00:00
screenshot
This commit is contained in:
parent
469bae9370
commit
65914739f5
2
Podfile
2
Podfile
@ -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
|
||||
|
12
Podfile.lock
12
Podfile.lock
@ -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
|
||||
|
@ -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
302
V2rayU/HttpServer.swift
Normal 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?")
|
||||
}
|
||||
}
|
@ -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
BIN
screenshot/1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 610 KiB |
BIN
screenshot/2.png
Normal file
BIN
screenshot/2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 150 KiB |
BIN
screenshot/3.png
Normal file
BIN
screenshot/3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 151 KiB |
BIN
screenshot/4.png
Normal file
BIN
screenshot/4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 100 KiB |
BIN
screenshot/5.png
Normal file
BIN
screenshot/5.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 116 KiB |
Loading…
x
Reference in New Issue
Block a user