Learning Golang: From 0 to multithreading in 3 days thanks to ChatGPT
Some of you may already know about my tool byp4xx, a simple script to bypass 40X/HTTP responses that uses different methodologies. It started being a simple bash script using cUrl but at some point I decided to move to Python. Just a personal challenge, I never wrote anything in Python, it was not needed at all because it still heavily relies on cUrl so I wouldn’t gain any advantage of it. Once the python code was complete, a couple of features that I would like to implement remained still in progress for several months: files as input and multithreading. I’ve just checked a couple of articles on how to implement multithreading in Python but even the most simple examples were not intuitive at first sight, so these features remains in the backlog for a while.
But then, some day, ChatGPT arose. As most of you, I spent several hours testing this new beast, challenging it, and it was impressive! Knowing its capabilities, it could help me to add the remaining features, but they were not my only backlog task. For some time I wanted to learn Golang but I never found time to benefit from my KodeKloud suscription, which has a very decent Golang course. Start to learn a new programming language in a traditional way could be boring as ****: data types, how to define variables, how declare a condition, operators, loops… Good to know, but if you have some previous background in any other language then surely you could face the new syntax. This was a great opportunity to kill two birds with one stone: learn Go and move byp4xx to it implementing those remaining features.
ChatGPT, do it for me
The approach was simple: spliting up my Python code into slices and ask ChatGPT to solve these little problems.
Create a program in golang that takes multiple arguments. The last argument must be an url and must start with “http://” or “https://”. The rest of the previous arguments will be saved in a variable called “options”
The provided code for this simple statement was quite decent and show me some basics of Golang:
- Use := for new variables and = to change the value for an existing one
- fmt.Println() for printing! Good!
- How to import multiple dependencies
- Conditional statements
- How to use net/http library
I didn’t want to use net/http, I still needed to rely on cUrl just in any case some user would like to set timeouts, custom headers, mTLS, proxy… or anything else that could be hard to implement using this library.
I continued the chat explicitly demanding to avoid net/http and using cUrl. This deleted the library from the import list and add os/exec. Now, move on to set the cUrl call into a function:
Create a new function called “curl_code_response” that will expect two parameters: options and url. Do not use net/http library, instead of use curl of the underlying system and run curl appending the options and the url variable values. Return only the http code response (200, 300… or anyting else). call this function from main()
Great! Now I had cUrl function as I declared in natural language and I can see how functions and parameters have to be declared in Go. Now ChatGPT add some colors, please
Now add colors depending on the return code: green if response code is 200, orange if it’s 30X and red for the rest of the http responses
Delicious! Got my fancy coloured results! Let’s gonna try to add some new features…
Now the program has to check if the last parameter is a valid URL or an existent file in the current working directory. If it is a url then act as usual in the previous code. If the last parameter does not start with “http://” or “https://” then consider it a file, check if this exists and confirm that all the lines included in that file start by “http://” or “https://”. Then read each line and call curl_code_response for each line.
Sure, this worked and now I’ve learnt how to read files and for loops in go! Achivement unlocked!
From here I had the minimal structure to reproduce the same code as I had in Python and I was just fine-tunning some minor mistakes in the code provided by ChatGPT. Also, I felt already pretty confident about the basics in Go, and this it was just about in a few hours. It took some time to import the same cUrl requests but that was the easy part.
From now on, things get complicated.
ChatGPT, please, do your work!
I already had a functional code and the app was working as expected, mostly. The next challenge would be implementing multithreads. I asked ChatGPT in multiple ways to implement multithreading in the code but none of them worked as desired. Mainly, ChatGPT reproduced the most simple examples of multithreading that didn´t fit at all with my code. To put into context, most of solutions served by ChatGPT where just a loop calling the same function n times, but my requirements were a litte bit more complex:
- I need to call the same function with different values.
- Eventually, these values cannot be added into a wordlist and has to be set in the code.
- The amount of calls to cUrl function is not a constant value, so I cannot set a constant value for a WaitingGroup.
- Using only goroutines turn my tool into a potential DoS tool.
- Goroutines work at speed of light, need to add semaphores.
- Again, I need to restrict the size of the semaphore and I didn’t want to hardcode it.
I’ve tried to be the most specific and detailed as possible to bring ChatGPT all this context and every single detail of the feature that I want to implement, but soon I realized that I reached the limit of ChatGPT comprehension on complex and custom tasks, so that was time to exchange from OpenAI to HumanI (human intelligence)
ChatGPT, I trusted you, but you don’t serve to me anymore!
So basically, at that point ChatGPT was useless. Time to check Golang tutorials, stackoverflow and other articles in Medium written by human beings. I adquired the base line of Go already, so I just need to move to a more advanced topic such as multithreading. Just a brief description of what I learned:
- Needed to define a waiting group (kind of a buffer for goroutines)
- wg.Add(X): Expect X goroutines
- defer wg.Done(): Complete a goroutine
- wg.Wait(): Wait that X routines have finished
- If Add > Done, Wait will be never reached
- If Add < Done, Wait will be reached in X iterations, the pending goroutines will be lost.
- You can tame goroutines workload, but it is almost impossible to sort them in a FIFO queue.
- Add should be equal to Done to reach Wait function gracefully
So, as you may wonder, working with all these pieces in a mutable environment with a changeable amount of goroutines was not easy for a newcomer. Additionally, as I said, goroutines work at speed of light so waiting groups were not enough to restrict the workload of goroutines. At that point I discovered semaphores, which is basically a queue, and how they could aid me to fence these threads.
A few hours later I achieved to implement goroutines with semaphores in a kind of useful way, adding a powerfull -and dangerous- multithreading feature to byp4xx and also the chance to control the rate limit of the requests.
For sure, you won’t find in byp4xx the most aesthetic and polished Go code out there. I can write code but I do not consider myself as a programmer cause I’ve never work on that role. But for me it has been very impressive how on a few days I managed, thanks to ChatGPT, to write something functional on a new language on which I was unable to write not even a simple “Hello World!”. At the end, this is the point: I would like to highlight not only the possibilities of ChatGPT by itself, but also the chances that brings us to boost our skills and knowledge if we know how to take advantage of it.