// Package protocol defines what is sent accross the TCP connection between the // router and service. It provides primitives for serializing various integer // types and data structures, as well as entire messages for the protocols it // supports. package protocol import "io" import "fmt" import "sync" // Message represents a message that can be sent along an io.Writer, and read // back when it comes out the other end. All messages must prefix themselves // with a Type code. type Message interface { // Send serializes the message to a writer, prefixed with its Type code. Send (io.Writer) error } // Serializer represents anything who's *content* can be serialized to a byte // stream. This does not include type information (such as a message Type code // prefix). type Serializer interface { // Serialize serializes the object's content to a writer. Serialize (io.Writer) error } // ID is a number that identifies an ongoing sequence. It is similar to a cookie // in the X11 protocol. All messages that are a part of the same sequence have // the same ID. ID zero is the initial ID which is generated by the service upon // sending a MessageLogin to the router. The corresponding sequence must always // be treated as active as long as the connection is open, and must never be // reused for something else. // // If the leftmost bit of an ID is set, that means it was generated by // the router. If the leftmost bit of an ID is unset, that means it was // generated by the service. When generating IDs, the service will start at 1 // and progressively increment the ID number. If the next ID would have its // leftmost bit set, it must loop back around to 1. Similarly, the router must // start at the maximum unsigned 32 bit integer (0xFFFFFFFF) and decrement until // the leftmost bit of the ID would be unset, and then loop back around to the // initial value. // // If either router or service generate an ID that is currently // in use by a sequence, that ID must be skipped over. For this reason, both // router and service must maintain a list of ongoing sequences along with their // active IDs. These lists (and corresponding generators) must be // connection-specific. // // Sequences are implicitly started by a message of the first type in any // particular protocol sector (see the documentation for Type in this package), // and must be sent with a newly generated ID as described above. The sequence // must consist only of messages that are part of the same protocol sector as // the message that started it (i.e. you cannot send a MessageGeminiResponse in // a sequence that was started by a MessageHTTPRequest). The only exception to // this is MessageStatus, which can be sent in any context. If this rule is // violated in any other capacity, the router or service should send a // MessageStatus with StatusBadMessageType and close the connection. // // The termination of any given sequence is determined by the rules of the // particular protocol sector it is a part of. type ID uint32 const RouterIDStart ID = 0xFFFFFFFF const ServiceIDStart ID = 0 // ReadID reads an ID from a Reader. func ReadID (reader io.Reader) (id ID, err error) { untyped, err := ReadU32(reader) return ID(untyped), err } // Serialize writes an ID to a Writer. func (id ID) Serialize (writer io.Writer) error { return WriteU32(writer, uint32(id)) } // IsRouter returns true if this ID was generated by a router. func (id ID) IsRouter () bool { return id >> 31 == 1 } // IsService returns true if this ID was generated by a service. func (id ID) IsService () bool { return !id.IsRouter() } // IDFactory generates IDs and keeps track of which ones are active. Its methods // can be called concurrently. type IDFactory struct { lock sync.Mutex next ID router bool active map[ID] struct { } } // NewRouterIDFactory returns a new IDFactory that generates router IDs. func NewRouterIDFactory () (factory *IDFactory) { factory = new(IDFactory) factory.active = make(map[ID] struct { }) factory.router = true factory.next = RouterIDStart return } // NewServiceIDFactory returns a new IDFactory that generates service IDs. func NewServiceIDFactory () (factory *IDFactory) { factory = new(IDFactory) factory.active = make(map[ID] struct { }) factory.next = ServiceIDStart return } // Next generates a new ID and returns it. The ID will be marked as active, and // a call to Free() is required to mark it as inactive when its corresponding // sequence has completed. func (factory *IDFactory) Next () ID { // FIXME: this is not the best way of doing it. it is unlikely that this // will ever cause problems, but the possibility is there. factory.lock.Lock() defer factory.lock.Unlock() id := factory.next if factory.router { next := id - 1 for factory.isActive(id) { next -- if next.IsService() { next = RouterIDStart } } factory.next = next } else { next := id + 1 for factory.isActive(id) { next ++ if next.IsRouter() { next = ServiceIDStart } } factory.next = next } return id } // Free marks an ID as inactive. Note that calling Free(0) is in violation of // the protocol. func (factory *IDFactory) Free (id ID) { factory.lock.Lock() defer factory.lock.Unlock() delete(factory.active, id) } func (factory *IDFactory) isActive (id ID) bool { _, active := factory.active[id] return active } // Type is an 8-bit code that comes before each message, and determines which // kind of message it is. The 256 type code values are divided into eight // 32-code sectors, where each sector corresponds to a different protocol. type Type uint8; const ( // Initial sector TypeNone Type = iota + 0x00 TypeLogin TypeStatus // HTTP/HTTPS protocol sector TypeHTTPRequest Type = iota + 0x20 TypeHTTPResponse TypeHTTPBodyRequest TypeHTTPBodySegment TypeHTTPBodyEnd // Gemini protocol sector TODO implement TypeGeminiRequest Type = iota + 0x40 TypeGeminiResponse TypeGeminiBodySegment TypeGeminiBodyEnd // Database access sector TODO implement TypeDatabaseGet Type = iota + 0x40 TypeDatabaseSet TypeDatabaseValue ) // ReadType reads a Type code from a Reader. func ReadType (reader io.Reader) (ty Type, err error) { untyped, err := ReadU8(reader) return Type(untyped), err } // Serialize writes a Type code to a Writer. func (ty Type) Serialize (writer io.Writer) error { return WriteU8(writer, uint8(ty)) } // String returns the name of the type as a string. func (ty Type) String () string { switch ty { case TypeLogin: return "TypeLogin" case TypeStatus: return "TypeStatus" case TypeHTTPRequest: return "TypeHTTPRequest" case TypeHTTPResponse: return "TypeHTTPResponse" case TypeHTTPBodyRequest: return "TypeHTTPBodyRequest" case TypeHTTPBodySegment: return "TypeHTTPBodySegment" case TypeHTTPBodyEnd: return "TypeHTTPBodyEnd" case TypeGeminiRequest: return "TypeGeminiRequest" case TypeGeminiResponse: return "TypeGeminiResponse" case TypeGeminiBodySegment: return "TypeGeminiBodySegment" case TypeGeminiBodyEnd: return "TypeGeminiBodyEnd" } return fmt.Sprintf("Type(%d)", ty) } // ReadMessage reads a Type code and its corresponding message from a Reader. // The type of message can be determined using a type switch. func ReadMessage (reader io.Reader) (Message, error) { ty, err := ReadType(reader) if err != nil { return nil, err } switch ty { case TypeLogin: return ReadMessageLogin(reader) case TypeStatus: return ReadMessageStatus(reader) case TypeHTTPRequest: return ReadMessageHTTPRequest(reader) case TypeHTTPResponse: return ReadMessageHTTPResponse(reader) case TypeHTTPBodyRequest: return ReadMessageHTTPBodyRequest(reader) case TypeHTTPBodySegment: return ReadMessageHTTPBodySegment(reader) case TypeHTTPBodyEnd: return ReadMessageHTTPBodyEnd(reader) case TypeGeminiRequest: return ReadMessageGeminiRequest(reader) case TypeGeminiResponse: return ReadMessageGeminiResponse(reader) case TypeGeminiBodySegment: return ReadMessageGeminiBodySegment(reader) case TypeGeminiBodyEnd: return ReadMessageGeminiBodyEnd(reader) } return nil, StatusBadMessageType }