Skip to content

Commit 364a2f8

Browse files
committed
Initial commit to move all my solved challenges from "msharran/labs"
repo to here.
1 parent 988add6 commit 364a2f8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+4601
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
.zig-cache/
22
zig-out/
3+
target
4+
.ruby-lsp
5+
.DS_Store

curl/cc-go-curl/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
curl

curl/cc-go-curl/Makefile

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
build:
2+
go build

curl/cc-go-curl/Readme.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
```bash
3+
projects/cc-go-curl [main●] » ./curl http://eu.httpbin.org/get -v
4+
* Connecting to eu.httpbin.org (************) port 80
5+
> GET /get HTTP/1.1
6+
> Connection: close
7+
> Host: eu.httpbin.org
8+
> Accept: */*
9+
>
10+
< HTTP/1.1 200 OK
11+
< Connection: close
12+
< Server: gunicorn/19.9.0
13+
< Access-Control-Allow-Origin: *
14+
< Access-Control-Allow-Credentials: true
15+
< Date: Tue, 04 Jun 2024 12:52:37 GMT
16+
< Content-Type: application/json
17+
< Content-Length: 225
18+
<
19+
20+
{ "args": {}, "headers": { "Accept": "*/*", "Host": "eu.httpbin.org", "X-Amzn-Trace-Id": "Root=1-665f0e15-54ac009005cb48ec1df2711f" }, "origin": "************", "url": "http://eu.httpbin.org/get"}
21+
```

curl/cc-go-curl/go.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module curl
2+
3+
go 1.22.1
4+
5+
require github.com/spf13/pflag v1.0.5 // indirect

curl/cc-go-curl/go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
2+
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package http
2+
3+
type Headers map[string]string
4+
5+
func (h Headers) Set(k, v string) {
6+
h[k] = v
7+
}
8+
9+
func (h Headers) Get(k string) string {
10+
return h[k]
11+
}

curl/cc-go-curl/internal/http/http.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package http
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"io"
7+
"log"
8+
"net"
9+
)
10+
11+
func Do(r *Request) (*Response, error) {
12+
reqb, err := writeWireFormat(r)
13+
if err != nil {
14+
return nil, fmt.Errorf("writeWireFormat error: %v", err)
15+
}
16+
17+
ip, err := net.ResolveIPAddr("ip", r.URL.Host)
18+
if err != nil {
19+
log.Fatalf("unable to resolve IP address: %v", err)
20+
}
21+
log.Printf("* Trying %s...", ip.IP.String())
22+
23+
conn, err := net.Dial("tcp", r.Addr())
24+
if err != nil {
25+
return nil, fmt.Errorf("tcp dial error: %v", err)
26+
}
27+
defer conn.Close()
28+
29+
log.Printf("* Connected to %s (%s) port %s", r.URL.Host, ip.IP.String(), r.URL.Port)
30+
log.Printf("%s", r)
31+
32+
_, err = io.Copy(conn, reqb)
33+
if err != nil {
34+
return nil, fmt.Errorf("conn write error: %v", err)
35+
}
36+
37+
respb := new(bytes.Buffer)
38+
_, err = io.Copy(respb, conn)
39+
if err != nil {
40+
return nil, fmt.Errorf("conn read error: %v", err)
41+
}
42+
43+
resp, err := readWireFormat(respb)
44+
if err != nil {
45+
return nil, fmt.Errorf("readWireFormat error: %v", err)
46+
}
47+
log.Printf("%s", resp)
48+
log.Printf("* Connection #0 to host %s left intact", r.URL.Host)
49+
50+
return resp, nil
51+
}
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package http
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"strings"
7+
)
8+
9+
func NewRequest(method string, url *URL, body io.Writer) *Request {
10+
if url == nil {
11+
panic("NewRequest: url cannot be nil")
12+
}
13+
14+
r := &Request{
15+
URL: url,
16+
Method: method,
17+
Body: nil,
18+
}
19+
r.Headers = Headers{
20+
"Host": url.Host,
21+
"Accept": "*/*",
22+
"Connection": "close",
23+
}
24+
25+
return r
26+
}
27+
28+
type Request struct {
29+
URL *URL
30+
Method string
31+
Headers Headers
32+
Body io.Reader
33+
}
34+
35+
func (r *Request) Addr() string {
36+
return r.URL.Host + ":" + r.URL.PortOrDefault()
37+
}
38+
39+
func (r *Request) String() string {
40+
sb := new(strings.Builder)
41+
42+
fmt.Fprintf(sb, "> %s %s %s%s", r.Method, r.URL.Path, HttpVersion, CRLF)
43+
h := r.Headers
44+
for k, v := range h {
45+
fmt.Fprintf(sb, "> %s: %s%s", k, v, CRLF)
46+
}
47+
fmt.Fprintf(sb, ">%s", CRLF)
48+
return sb.String()
49+
}
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package http
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"io"
7+
"strings"
8+
)
9+
10+
type Response struct {
11+
HTTPVersion string
12+
Headers Headers
13+
Body io.Reader
14+
StatusCode int
15+
StatusText string
16+
}
17+
18+
// Response errors
19+
var (
20+
ErrEmptyFirstLine = errors.New("response: first line is empty")
21+
ErrInvalidFirstLine = errors.New("response: first line is invalid")
22+
ErrHTTPCodeParse = errors.New("response: failed to parse status code")
23+
)
24+
25+
func (r *Response) String() string {
26+
sb := new(strings.Builder)
27+
28+
fmt.Fprintf(sb, "< %s %d %s%s", r.HTTPVersion, r.StatusCode, r.StatusText, CRLF)
29+
h := r.Headers
30+
for k, v := range h {
31+
fmt.Fprintf(sb, "< %s: %s%s", k, v, CRLF)
32+
}
33+
fmt.Fprintf(sb, "<%s", CRLF)
34+
return sb.String()
35+
}

curl/cc-go-curl/internal/http/url.go

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package http
2+
3+
import (
4+
"errors"
5+
"strings"
6+
)
7+
8+
var (
9+
ports = map[string]string{
10+
"http": "80",
11+
"https": "443",
12+
}
13+
)
14+
15+
type URL struct {
16+
Scheme string
17+
Host string
18+
Port string
19+
Path string
20+
}
21+
22+
func (u *URL) PortOrDefault() string {
23+
if u.Port == "" {
24+
return ports[u.Scheme]
25+
}
26+
27+
return u.Port
28+
}
29+
30+
func ParseURL(url string) (*URL, error) {
31+
scheme, rest, ok := strings.Cut(url, "://")
32+
if !ok {
33+
return nil, errors.New("invalid URL format")
34+
}
35+
36+
if _, ok := ports[scheme]; !ok {
37+
return nil, errors.New("unsupported protocol scheme")
38+
}
39+
40+
u := new(URL)
41+
u.Scheme = scheme
42+
43+
addr, rest, _ := strings.Cut(rest, "/")
44+
u.Path = "/" + rest
45+
46+
host, port, ok := strings.Cut(addr, ":")
47+
if !ok || port == "" {
48+
port = ports[scheme]
49+
}
50+
u.Host = host
51+
u.Port = port
52+
53+
return u, nil
54+
}
+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package http
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"fmt"
7+
"io"
8+
"strings"
9+
)
10+
11+
const (
12+
CRLF = "\r\n"
13+
HttpVersion = "HTTP/1.1"
14+
)
15+
16+
func readWireFormat(buf *bytes.Buffer) (*Response, error) {
17+
// parse http response in wire format to Response struct
18+
resp := new(Response)
19+
resp.Headers = make(Headers)
20+
body := new(bytes.Buffer)
21+
resp.Body = body
22+
23+
scanner := bufio.NewScanner(buf)
24+
if !scanner.Scan() && scanner.Err() != nil {
25+
return nil, fmt.Errorf("failed to read first line: %v", scanner.Err())
26+
}
27+
28+
firstLine := scanner.Text()
29+
_, err := fmt.Sscanf(firstLine, "%s %d %s", &resp.HTTPVersion, &resp.StatusCode, &resp.StatusText)
30+
if err != nil {
31+
return nil, fmt.Errorf("failed to parse first line: %v", err)
32+
}
33+
34+
if resp.HTTPVersion != HttpVersion {
35+
return nil, fmt.Errorf("unsupported HTTP version: %s", resp.HTTPVersion)
36+
}
37+
38+
// parse headers
39+
for scanner.Scan() {
40+
line := scanner.Text()
41+
42+
if line == "" {
43+
break
44+
}
45+
46+
k, v, found := strings.Cut(line, ":")
47+
if !found {
48+
return nil, fmt.Errorf("malformed header: %s", line)
49+
}
50+
51+
resp.Headers.Set(k, v)
52+
}
53+
54+
// parse body
55+
for scanner.Scan() {
56+
line := scanner.Text()
57+
if line == "" {
58+
break
59+
}
60+
61+
_, err := io.WriteString(body, line)
62+
if err != nil {
63+
return nil, fmt.Errorf("failed to write body: %v", err)
64+
}
65+
}
66+
67+
if err := scanner.Err(); err != nil {
68+
return nil, fmt.Errorf("scanner error: %v", err)
69+
}
70+
71+
return resp, nil
72+
}
73+
74+
func writeWireFormat(req *Request) (*bytes.Buffer, error) {
75+
buf := new(bytes.Buffer)
76+
_, err := fmt.Fprintf(buf, "%s %s %s%s", req.Method, req.URL.Path, HttpVersion, CRLF)
77+
if err != nil {
78+
return nil, err
79+
}
80+
81+
_, err = fmt.Fprintf(buf, "Host: %s%s", req.URL.Host, CRLF)
82+
if err != nil {
83+
return nil, err
84+
}
85+
86+
for k, v := range req.Headers {
87+
_, err = fmt.Fprintf(buf, "%s: %s%s", k, v, CRLF)
88+
if err != nil {
89+
return nil, err
90+
}
91+
}
92+
93+
_, err = fmt.Fprintf(buf, "Connection: close%s", CRLF)
94+
if err != nil {
95+
return nil, err
96+
}
97+
_, err = io.WriteString(buf, CRLF)
98+
if err != nil {
99+
return nil, err
100+
}
101+
102+
if req.Body != nil {
103+
_, err = io.Copy(buf, req.Body)
104+
if err != nil {
105+
return nil, err
106+
}
107+
}
108+
109+
_, err = io.WriteString(buf, CRLF)
110+
if err != nil {
111+
return nil, err
112+
}
113+
return buf, nil
114+
}

0 commit comments

Comments
 (0)