add examples about protecting internet-facing httpds
* request body size limit * request timeout
This commit is contained in:
parent
623df2b3cf
commit
c46038c51d
145
README.md
145
README.md
|
@ -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.
|
||||
|
@ -115,10 +116,10 @@ Feedback and suggestions are not only welcome but explicitly encouraged.
|
|||
in a function called `main` and call `main()` at the end of the script
|
||||
using the standard invocation:
|
||||
|
||||
```python
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
```python
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
## Golang
|
||||
|
||||
|
@ -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,63 +653,63 @@ 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
|
||||
reduce GC overhead and improve performance in high-throughput scenarios.
|
||||
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
|
||||
lead to memory leaks. Use appropriate buffer sizes based on the
|
||||
application's needs.
|
||||
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
|
||||
panic and ensures graceful shutdowns.
|
||||
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
|
||||
allocations. Use pointer receivers for large structs or when mutating
|
||||
the receiver.
|
||||
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
|
||||
high memory consumption and increased complexity.
|
||||
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
|
||||
met with simple mutexes and channels.
|
||||
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
|
||||
code that is hard to understand and maintain. Prefer type-safe
|
||||
solutions.
|
||||
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
|
||||
for request-scoped values like deadlines, cancellation signals, and
|
||||
authentication tokens.
|
||||
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
|
||||
traces for debugging and logging purposes.
|
||||
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
|
||||
used with both `*testing.T` and `*testing.B`.
|
||||
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
|
||||
form of composition that can simplify code reuse.
|
||||
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
|
||||
relying on implicit interfaces. This makes the intended use of
|
||||
interfaces clearer and the code more maintainable.
|
||||
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.
|
||||
|
||||
# Author
|
||||
|
||||
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue