Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send response after reading request from browser with Socket ? (I'm using SwiftSocket)

I am new to this networking in iOS so doesn't know all the concepts so well and I'm making an app which allows to show data on browser which is on device, For that I'm creating a socket-port and using SwiftSocket library

override func viewDidLoad() {
    super.viewDidLoad()
    let ip = String(getAddress(for: .wifi)!)
    self.host = ip
    print("Getting IP address of wifi\n\(self.host)\n")
    self.selfserver()
}

I think following function selfserver() initialises the server and wait for the client to connect

func selfserver()
{
    let server = TCPServer(address: self.host, port: Int32(port))
    switch server.listen() {
    case .success:
        while true {
            if let client2 = server.accept() {
                print("CLIENT ACCEPTED ....")
                 self.startconnection(senderclient: client2)
            } else {
                print("accept error")
            }
        }
    case .failure(let error):
        print(error)
    }
}

When client will try to connect following function senderclient(senderclient: TCPClient) will be called and in the response I'm sending index.html file which is saved in htmlfileURL key in Userdefaults

func startconnection(senderclient: TCPClient) {
    print("CLIENT ADD\n\(senderclient.address)\n")
    let filePath = UserDefaults.standard.url(forKey: "htmlfileURL")
    // Converting `index.html` in bytes to send
    var bytes = [UInt8]()
    if let data = NSData(contentsOfFile: filePath!.path) {
        var buffer = [UInt8](repeating: 0, count: data.length)
        data.getBytes(&buffer, length: data.length)
        bytes = buffer
        }

    senderclient.send(string: "HTTP/1.1 200 OK\n" +
        "Content-Length: \(bytes.count)\n" +
        "Connection: close\n" +
        "Content-Type: text/html\n" +
        "\n")
    switch senderclient.send(data: bytes) {
    case .success:
        let data = senderclient.read(1024*10)
        if let d = data {
            if let str = String(bytes: d, encoding: .utf8) {
                print("STR\n\(str)\n")
                // If I'm not wrong Here I should get the response 
            }
        }

    case .failure(let erro):
        print(erro)
    }
}

My problem is I am getting all request headers and after filtering I am also getting which filename and content-type the request header requires but I don't know how to send those file after receiving and reading the request header in reponse ..

enter image description here

As you can see in the above screenshot In console area , you can see I'm getting a request of styles.afcc5f0641cf44266b1b.css file...I just don't know how to send that file to the browser when it requests(I have full path of the file)

Thank You

like image 372
Nayan Dave Avatar asked Jan 21 '20 05:01

Nayan Dave


1 Answers

NOTE:- I'm Still Searching for the Right answer which can be very useful to everyone as Socket in Swift is quite difficult concept to implement

//Iniiating Two variable which are initially empty to check if the loop is running first time
var loopablestring = String()
var loopableExtension = String()
//which was necessary here because otherwise only 'index.html' file will be sent in response to every request header    

//MARK:- Start connection with Client Socket Function
func startconnection(senderclient: TCPClient) {

    let filePath = Bundle.main.resourceURL
    var FilePath = String()
    var FileBytes = [Byte]()
    var ContType = String()
    
    //Initially this code will be called
    if self.loopablestring == "" {
        FilePath = filePath!.appendingPathComponent("index.html").path
        FileBytes = self.getBytesFromFile(fromPath: FilePath)
        self.loopableExtension = "html"
        ContType = self.getMimeType(file: self.loopableExtension)
    } else {
        FilePath = filePath!.appendingPathComponent(loopablestring).path
        FileBytes = self.getBytesFromFile(fromPath: FilePath)
        ContType = self.getMimeType(file: self.loopableExtension)
    }
    //sending requested file(if any or sending 'index.html')
    senderclient.send(string: "HTTP/1.1 200 OK\n" +
        "Content-Length: \(FileBytes.count)\n" +
        "Connection: close\n" +
        "Content-Type: \(ContType)\n" +
        "\n")

    print("Sending File Of Which Content-Type\t\(ContType)\n And Path\t\(FilePath)\nAnd Pure File Name\t\(loopablestring)\nWith Extension\t\(loopableExtension)\n")

    switch senderclient.send(data: FileBytes) {
    case .success:
        //print("SUCCESS")
        if let responseBytes = senderclient.read(1024*10) {
            let responseString = String(bytes: responseBytes, encoding: .utf8)!
            print("\nRespons String which contains Headers\n\(responseString)\n\n")

            let HeaderLines = responseString.components(separatedBy: .newlines)[0]
            let separator = HeaderLines.components(separatedBy: .whitespaces)[1]
            //print("Only first line of the Header\n\(HeaderLines)\n\n")

            if separator != "/" {
            //If we don't get Empty or nil value in request header then this code will run

            let purefilename = separator.components(separatedBy: "/")[1]
            //print("Pure File Name filtered from Header's First Line \n\(purefilename)\n")

                print("ALLOCATED FILE \n\(purefilename)\n")

                //giving that two variable values so in next loop's it won't go in `if self.loopablestring == ""` condition
                self.loopablestring = purefilename
                self.loopableExtension = self.getMimeType(file: separator)
                //giving that two variable values so in next loop's it won't go in `if self.loopablestring == ""` condition
            } else {
                print("CAME HERE to be closed")
            }
        }

    case .failure(let error):
        print("FAILED because of \(error)")
    }
    print(" \t\t AFTER THE LOOP \n\n\n")
    senderclient.close()
}

//To return Content Type to Browser (In response Header)
func getContentType(filename: String) -> String {
    do {
        if filename == "js" {
            return "application/javascript"
        } else if filename == "html" {
            return "text/html"
        } else if filename == "css" {
            return "text/css"
        } else if filename == "ico" || filename == "png" || filename == "PNG" || filename == "jpg"  || filename == "JPG" || filename == "jpeg" || filename == "JPEG" {
            return "image/png"
        } else {
            return "text/html"
        }
    } catch {
        print("Something is Wrong!!")
    }
}

//TO Get Extension without (.)DOT
func getMimeType(file: String) -> String {
    let ext = file.components(separatedBy: ".")
    //print("all extensions\n\(ext)\n")
    return ext.last!
}

After implementing above code I'm able to get all the Request Headers and send the Response But there is only one issue left :(See the below Screenshot)

enter image description here

Now, as you can see in the image that I'm able to read all the response header and I'm sending the requested files too But issue (because of loop or I don't know what) is the response file I send is going in the next request instead of the current request header

ex:

It shows null data in the first requested file and the data/file I'm sending for first styles.afcc5f0641cf44266b1b.css is going in the next(second) requested header of runtime.26209474bfa8dc87a77c.js and because of this irregularity, my last file favicon.ico is not even being sent

like image 145
Nayan Dave Avatar answered Oct 31 '22 13:10

Nayan Dave