add examples about protecting internet-facing httpds

* request body size limit
* request timeout
This commit is contained in:
Jeffrey Paul 2024-06-10 05:32:48 -07:00
parent 623df2b3cf
commit c46038c51d

103
README.md
View File

@ -1,6 +1,7 @@
# sneak/styleguide
The following is the first released version of my personal code styleguide.
There are many like it, but this one is mine.
Only the Go portion is "complete". The others are mostly just
placeholders.
@ -237,8 +238,9 @@ Feedback and suggestions are not only welcome but explicitly encouraged.
repo root.
1. If you are writing a multi-module project, put all `.go` files in a
`pkg/` or `internal/` directory. This is to keep the root clean and to
make it easier to see what is a library and what is a binary.
`pkg/` or `internal/` subdirectory. `internal/` is for modules used
only by the current repo, and `pkg/` is for modules that can be consumed
externally. This is to keep the repo root as clean as possible.
1. Binaries go in `cmd/` directories. Each binary should have its own
directory. This is to keep the root clean and to make it easier to see
@ -576,18 +578,73 @@ Feedback and suggestions are not only welcome but explicitly encouraged.
## Other Golang Tips and Best Practices (Optional)
1. For any internet-facing http server, set appropriate timeouts and limits
to protect against slowloris attacks or huge uploads that can consume
server resources even without authentication.
Example to limit request body size:
```go
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Limit the request body to 10MB
r.Body = http.MaxBytesReader(w, r.Body, 10<<20)
if err := r.ParseForm(); err != nil {
http.Error(w, "Request body too large", http.StatusRequestEntityTooLarge)
return
}
fmt.Fprintf(w, "Hello, World!")
})
http.ListenAndServe(":8080", nil)
}
```
Example to set appropriate timeouts:
```go
package main
import (
"net/http"
"time"
)
func main() {
server := &http.Server{
Addr: ":8080",
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
Handler: http.DefaultServeMux,
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
})
server.ListenAndServe()
}
```
1. When passing channels to goroutines, use read-only (`<-chan`) or
write-only (`chan<-`) channels to communicate the direction of data flow
clearly.
2. Use `io.MultiReader` to concatenate multiple readers and
1. Use `io.MultiReader` to concatenate multiple readers and
`io.MultiWriter` to duplicate writes to multiple writers. This can
simplify the handling of multiple data sources or destinations.
3. For simple counters and flags, use the `sync/atomic` package to avoid
1. For simple counters and flags, use the `sync/atomic` package to avoid
the overhead of mutexes.
4. When using mutexes, minimize the scope of locking to reduce contention
1. When using mutexes, minimize the scope of locking to reduce contention
and potential deadlocks. Prefer to lock only the critical sections of
code. Try to encapsulate the critical section in its own function or
method. Acquire the lock as the first line of the function, defer
@ -596,61 +653,61 @@ Feedback and suggestions are not only welcome but explicitly encouraged.
using mutexes in the middle of a function. In short, build atomic
functions.
5. Design types to be immutable where possible. This can help avoid issues
1. Design types to be immutable where possible. This can help avoid issues
with concurrent access and make the code easier to reason about.
6. Global state can lead to unpredictable behavior and makes the code
1. Global state can lead to unpredictable behavior and makes the code
harder to test. Use dependency injection to manage state.
7. Avoid using `init` functions unless absolutely necessary as they can
1. Avoid using `init` functions unless absolutely necessary as they can
lead to unpredictable initialization order and make the code harder to
understand.
8. Provide comments for all public interfaces explaining what they do and
1. Provide comments for all public interfaces explaining what they do and
how they should be used. This helps other developers understand the
intended use.
9. Be mindful of resource leaks when using `time.Timer` and `time.Ticker`.
1. Be mindful of resource leaks when using `time.Timer` and `time.Ticker`.
Always stop them when they are no longer needed.
10. Use `sync.Pool` to manage a pool of reusable objects, which can help
1. Use `sync.Pool` to manage a pool of reusable objects, which can help
reduce GC overhead and improve performance in high-throughput scenarios.
11. Avoid using large buffer sizes for channels. Unbounded channels can
1. Avoid using large buffer sizes for channels. Unbounded channels can
lead to memory leaks. Use appropriate buffer sizes based on the
application's needs.
12. Always handle the case where a channel might be closed. This prevents
1. Always handle the case where a channel might be closed. This prevents
panic and ensures graceful shutdowns.
13. For small structs, use value receivers to avoid unnecessary heap
1. For small structs, use value receivers to avoid unnecessary heap
allocations. Use pointer receivers for large structs or when mutating
the receiver.
14. Only use goroutines when necessary. Excessive goroutines can lead to
1. Only use goroutines when necessary. Excessive goroutines can lead to
high memory consumption and increased complexity.
15. Use `sync.Cond` for more complex synchronization needs that cannot be
1. Use `sync.Cond` for more complex synchronization needs that cannot be
met with simple mutexes and channels.
16. Reflection is powerful but should be used sparingly as it can lead to
1. Reflection is powerful but should be used sparingly as it can lead to
code that is hard to understand and maintain. Prefer type-safe
solutions.
17. Avoid storing large or complex data in context. Context should be used
1. Avoid storing large or complex data in context. Context should be used
for request-scoped values like deadlines, cancellation signals, and
authentication tokens.
18. Use `runtime.Callers` and `runtime.CallersFrames` to capture stack
1. Use `runtime.Callers` and `runtime.CallersFrames` to capture stack
traces for debugging and logging purposes.
19. Use the `testing.TB` interface to write helper functions that can be
1. Use the `testing.TB` interface to write helper functions that can be
used with both `*testing.T` and `*testing.B`.
20. Use struct embedding to reuse code across multiple structs. This is a
1. Use struct embedding to reuse code across multiple structs. This is a
form of composition that can simplify code reuse.
21. Prefer defining explicit interfaces in your packages rather than
1. Prefer defining explicit interfaces in your packages rather than
relying on implicit interfaces. This makes the intended use of
interfaces clearer and the code more maintainable.
@ -665,3 +722,5 @@ Feedback and suggestions are not only welcome but explicitly encouraged.
Do with it what you will. There is no warranty, express or implied,
including but not limited to merchantability or fitness for a particular
purpose. Use at your own risk.
# Credit