how to read "char* const *(*next)();"

博客围绕Go语言中变量声明展开解析。先关注变量名‘next’,其被括号包围,将其与括号内内容组合。接着分析括号外,依据规则确定函数括号优先级最高,得出‘next是指向返回…的函数的指针’,最后处理‘char * const’为指向字符的常量指针。

A. First, go to the variable name, "next", and note that it is directly enclosed by parentheses.

B1. So we group it with what else is in the parentheses, to get "next is a pointer to...".

B. Then we go outside the parentheses, and have a choice of a prefix asterisk, or a postfix pair of parentheses.

B2. Rule B.2 tells us the highest precedence thing is the function parentheses at the right, so we have "next is a pointer to a function returning…"

B3. Then process the prefix "*" to get "pointer to".

C. Finally, take the "char * const", as a constant pointer to a character.

这是arduinohtpclient库中的HttpClient.cpp的内容// Class to simplify HTTP fetching on Arduino // (c) Copyright 2010-2011 MCQN Ltd // Released under Apache License, version 2.0 #include "HttpClient.h" #include "b64.h" // Initialize constants const char* HttpClient::kUserAgent = "Arduino/2.2.0"; const char* HttpClient::kContentLengthPrefix = HTTP_HEADER_CONTENT_LENGTH ": "; const char* HttpClient::kTransferEncodingChunked = HTTP_HEADER_TRANSFER_ENCODING ": " HTTP_HEADER_VALUE_CHUNKED; HttpClient::HttpClient(Client& aClient, const char* aServerName, uint16_t aServerPort) : iClient(&aClient), iServerName(aServerName), iServerAddress(), iServerPort(aServerPort), iConnectionClose(true), iSendDefaultRequestHeaders(true) { resetState(); } HttpClient::HttpClient(Client& aClient, const String& aServerName, uint16_t aServerPort) : HttpClient(aClient, aServerName.c_str(), aServerPort) { } HttpClient::HttpClient(Client& aClient, const IPAddress& aServerAddress, uint16_t aServerPort) : iClient(&aClient), iServerName(NULL), iServerAddress(aServerAddress), iServerPort(aServerPort), iConnectionClose(true), iSendDefaultRequestHeaders(true) { resetState(); } void HttpClient::resetState() { iState = eIdle; iStatusCode = 0; iContentLength = kNoContentLengthHeader; iBodyLengthConsumed = 0; iContentLengthPtr = kContentLengthPrefix; iTransferEncodingChunkedPtr = kTransferEncodingChunked; iIsChunked = false; iChunkLength = 0; iHttpResponseTimeout = kHttpResponseTimeout; iHttpWaitForDataDelay = kHttpWaitForDataDelay; } void HttpClient::stop() { iClient->stop(); resetState(); } void HttpClient::connectionKeepAlive() { iConnectionClose = false; } void HttpClient::noDefaultRequestHeaders() { iSendDefaultRequestHeaders = false; } void HttpClient::beginRequest() { iState = eRequestStarted; } int HttpClient::startRequest(const char* aURLPath, const char* aHttpMethod, const char* aContentType, int aContentLength, const byte aBody[]) { if (iState == eReadingBody || iState == eReadingChunkLength || iState == eReadingBodyChunk) { flushClientRx(); resetState(); } tHttpState initialState = iState; if ((eIdle != iState) && (eRequestStarted != iState)) { return HTTP_ERROR_API; } if (iConnectionClose || !iClient->connected()) { if (iServerName) { if (!(iClient->connect(iServerName, iServerPort) > 0)) { #ifdef LOGGING Serial.println("Connection failed"); #endif return HTTP_ERROR_CONNECTION_FAILED; } } else { if (!(iClient->connect(iServerAddress, iServerPort) > 0)) { #ifdef LOGGING Serial.println("Connection failed"); #endif return HTTP_ERROR_CONNECTION_FAILED; } } } else { #ifdef LOGGING Serial.println("Connection already open"); #endif } // Now we're connected, send the first part of the request int ret = sendInitialHeaders(aURLPath, aHttpMethod); if (HTTP_SUCCESS == ret) { if (aContentType) { sendHeader(HTTP_HEADER_CONTENT_TYPE, aContentType); } if (aContentLength > 0) { sendHeader(HTTP_HEADER_CONTENT_LENGTH, aContentLength); } bool hasBody = (aBody && aContentLength > 0); if (initialState == eIdle || hasBody) { // This was a simple version of the API, so terminate the headers now finishHeaders(); } // else we'll call it in endRequest or in the first call to print, etc. if (hasBody) { write(aBody, aContentLength); } } return ret; } int HttpClient::sendInitialHeaders(const char* aURLPath, const char* aHttpMethod) { #ifdef LOGGING Serial.println("Connected"); #endif // Send the HTTP command, i.e. "GET /somepath/ HTTP/1.0" iClient->print(aHttpMethod); iClient->print(" "); iClient->print(aURLPath); iClient->println(" HTTP/1.1"); if (iSendDefaultRequestHeaders) { // The host header, if required if (iServerName) { iClient->print("Host: "); iClient->print(iServerName); if (iServerPort != kHttpPort && iServerPort != kHttpsPort) { iClient->print(":"); iClient->print(iServerPort); } iClient->println(); } // And user-agent string sendHeader(HTTP_HEADER_USER_AGENT, kUserAgent); } if (iConnectionClose) { // Tell the server to // close this connection after we're done sendHeader(HTTP_HEADER_CONNECTION, "close"); } // Everything has gone well iState = eRequestStarted; return HTTP_SUCCESS; } void HttpClient::sendHeader(const char* aHeader) { iClient->println(aHeader); } void HttpClient::sendHeader(const char* aHeaderName, const char* aHeaderValue) { iClient->print(aHeaderName); iClient->print(": "); iClient->println(aHeaderValue); } void HttpClient::sendHeader(const char* aHeaderName, const int aHeaderValue) { iClient->print(aHeaderName); iClient->print(": "); iClient->println(aHeaderValue); } void HttpClient::sendBasicAuth(const char* aUser, const char* aPassword) { // Send the initial part of this header line iClient->print("Authorization: Basic "); // Now Base64 encode "aUser:aPassword" and send that // This seems trickier than it should be but it's mostly to avoid either // (a) some arbitrarily sized buffer which hopes to be big enough, or // (b) allocating and freeing memory // ...so we'll loop through 3 bytes at a time, outputting the results as we // go. // In Base64, each 3 bytes of unencoded data become 4 bytes of encoded data unsigned char input[3]; unsigned char output[5]; // Leave space for a '\0' terminator so we can easily print int userLen = strlen(aUser); int passwordLen = strlen(aPassword); int inputOffset = 0; for (int i = 0; i < (userLen+1+passwordLen); i++) { // Copy the relevant input byte into the input if (i < userLen) { input[inputOffset++] = aUser[i]; } else if (i == userLen) { input[inputOffset++] = ':'; } else { input[inputOffset++] = aPassword[i-(userLen+1)]; } // See if we've got a chunk to encode if ( (inputOffset == 3) || (i == userLen+passwordLen) ) { // We've either got to a 3-byte boundary, or we've reached then end b64_encode(input, inputOffset, output, 4); // NUL-terminate the output string output[4] = '\0'; // And write it out iClient->print((char*)output); // FIXME We might want to fill output with '=' characters if b64_encode doesn't // FIXME do it for us when we're encoding the final chunk inputOffset = 0; } } // And end the header we've sent iClient->println(); } void HttpClient::finishHeaders() { iClient->println(); iState = eRequestSent; } void HttpClient::flushClientRx() { while (iClient->available()) { iClient->read(); } } void HttpClient::endRequest() { beginBody(); } void HttpClient::beginBody() { if (iState < eRequestSent) { // We still need to finish off the headers finishHeaders(); } // else the end of headers has already been sent, so nothing to do here } int HttpClient::get(const char* aURLPath) { return startRequest(aURLPath, HTTP_METHOD_GET); } int HttpClient::get(const String& aURLPath) { return get(aURLPath.c_str()); } int HttpClient::post(const char* aURLPath) { return startRequest(aURLPath, HTTP_METHOD_POST); } int HttpClient::post(const String& aURLPath) { return post(aURLPath.c_str()); } int HttpClient::post(const char* aURLPath, const char* aContentType, const char* aBody) { return post(aURLPath, aContentType, strlen(aBody), (const byte*)aBody); } int HttpClient::post(const String& aURLPath, const String& aContentType, const String& aBody) { return post(aURLPath.c_str(), aContentType.c_str(), aBody.length(), (const byte*)aBody.c_str()); } int HttpClient::post(const char* aURLPath, const char* aContentType, int aContentLength, const byte aBody[]) { return startRequest(aURLPath, HTTP_METHOD_POST, aContentType, aContentLength, aBody); } int HttpClient::put(const char* aURLPath) { return startRequest(aURLPath, HTTP_METHOD_PUT); } int HttpClient::put(const String& aURLPath) { return put(aURLPath.c_str()); } int HttpClient::put(const char* aURLPath, const char* aContentType, const char* aBody) { return put(aURLPath, aContentType, strlen(aBody), (const byte*)aBody); } int HttpClient::put(const String& aURLPath, const String& aContentType, const String& aBody) { return put(aURLPath.c_str(), aContentType.c_str(), aBody.length(), (const byte*)aBody.c_str()); } int HttpClient::put(const char* aURLPath, const char* aContentType, int aContentLength, const byte aBody[]) { return startRequest(aURLPath, HTTP_METHOD_PUT, aContentType, aContentLength, aBody); } int HttpClient::patch(const char* aURLPath) { return startRequest(aURLPath, HTTP_METHOD_PATCH); } int HttpClient::patch(const String& aURLPath) { return patch(aURLPath.c_str()); } int HttpClient::patch(const char* aURLPath, const char* aContentType, const char* aBody) { return patch(aURLPath, aContentType, strlen(aBody), (const byte*)aBody); } int HttpClient::patch(const String& aURLPath, const String& aContentType, const String& aBody) { return patch(aURLPath.c_str(), aContentType.c_str(), aBody.length(), (const byte*)aBody.c_str()); } int HttpClient::patch(const char* aURLPath, const char* aContentType, int aContentLength, const byte aBody[]) { return startRequest(aURLPath, HTTP_METHOD_PATCH, aContentType, aContentLength, aBody); } int HttpClient::del(const char* aURLPath) { return startRequest(aURLPath, HTTP_METHOD_DELETE); } int HttpClient::del(const String& aURLPath) { return del(aURLPath.c_str()); } int HttpClient::del(const char* aURLPath, const char* aContentType, const char* aBody) { return del(aURLPath, aContentType, strlen(aBody), (const byte*)aBody); } int HttpClient::del(const String& aURLPath, const String& aContentType, const String& aBody) { return del(aURLPath.c_str(), aContentType.c_str(), aBody.length(), (const byte*)aBody.c_str()); } int HttpClient::del(const char* aURLPath, const char* aContentType, int aContentLength, const byte aBody[]) { return startRequest(aURLPath, HTTP_METHOD_DELETE, aContentType, aContentLength, aBody); } int HttpClient::responseStatusCode() { if (iState < eRequestSent) { return HTTP_ERROR_API; } // The first line will be of the form Status-Line: // HTTP-Version SP Status-Code SP Reason-Phrase CRLF // Where HTTP-Version is of the form: // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT int c = '\0'; do { // Make sure the status code is reset, and likewise the state. This // lets us easily cope with 1xx informational responses by just // ignoring them really, and reading the next line for a proper response iStatusCode = 0; iState = eRequestSent; unsigned long timeoutStart = millis(); // Psuedo-regexp we're expecting before the status-code const char* statusPrefix = "HTTP/*.* "; const char* statusPtr = statusPrefix; // Whilst we haven't timed out & haven't reached the end of the headers while ((c != '\n') && ( (millis() - timeoutStart) < iHttpResponseTimeout )) { if (available()) { c = HttpClient::read(); if (c != -1) { switch(iState) { case eRequestSent: // We haven't reached the status code yet if ( (*statusPtr == '*') || (*statusPtr == c) ) { // This character matches, just move along statusPtr++; if (*statusPtr == '\0') { // We've reached the end of the prefix iState = eReadingStatusCode; } } else { return HTTP_ERROR_INVALID_RESPONSE; } break; case eReadingStatusCode: if (isdigit(c)) { // This assumes we won't get more than the 3 digits we // want iStatusCode = iStatusCode*10 + (c - '0'); } else { // We've reached the end of the status code // We could sanity check it here or double-check for ' ' // rather than anything else, but let's be lenient iState = eStatusCodeRead; } break; case eStatusCodeRead: // We're just waiting for the end of the line now break; default: break; }; // We read something, reset the timeout counter timeoutStart = millis(); } } else { // We haven't got any data, so let's pause to allow some to // arrive delay(iHttpWaitForDataDelay); } } if ( (c == '\n') && (iStatusCode < 200 && iStatusCode != 101) ) { // We've reached the end of an informational status line c = '\0'; // Clear c so we'll go back into the data reading loop } } // If we've read a status code successfully but it's informational (1xx) // loop back to the start while ( (iState == eStatusCodeRead) && (iStatusCode < 200 && iStatusCode != 101) ); if ( (c == '\n') && (iState == eStatusCodeRead) ) { // We've read the status-line successfully return iStatusCode; } else if (c != '\n') { // We must've timed out before we reached the end of the line return HTTP_ERROR_TIMED_OUT; } else { // This wasn't a properly formed status line, or at least not one we // could understand return HTTP_ERROR_INVALID_RESPONSE; } } int HttpClient::skipResponseHeaders() { // Just keep reading until we finish reading the headers or time out unsigned long timeoutStart = millis(); // Whilst we haven't timed out & haven't reached the end of the headers while ((!endOfHeadersReached()) && ( (millis() - timeoutStart) < iHttpResponseTimeout )) { if (available()) { (void)readHeader(); // We read something, reset the timeout counter timeoutStart = millis(); } else { // We haven't got any data, so let's pause to allow some to // arrive delay(iHttpWaitForDataDelay); } } if (endOfHeadersReached()) { // Success return HTTP_SUCCESS; } else { // We must've timed out return HTTP_ERROR_TIMED_OUT; } } bool HttpClient::endOfHeadersReached() { return (iState == eReadingBody || iState == eReadingChunkLength || iState == eReadingBodyChunk); }; long HttpClient::contentLength() { // skip the response headers, if they haven't been read already if (!endOfHeadersReached()) { skipResponseHeaders(); } return iContentLength; } String HttpClient::responseBody() { int bodyLength = contentLength(); String response; if (bodyLength > 0) { // try to reserve bodyLength bytes if (response.reserve(bodyLength) == 0) { // String reserve failed return String((const char*)NULL); } } // keep on timedRead'ing, until: // - we have a content length: body length equals consumed or no bytes // available // - no content length: no bytes are available while (iBodyLengthConsumed != bodyLength) { int c = timedRead(); if (c == -1) { // read timed out, done break; } if (!response.concat((char)c)) { // adding char failed return String((const char*)NULL); } } if (bodyLength > 0 && (unsigned int)bodyLength != response.length()) { // failure, we did not read in response content length bytes return String((const char*)NULL); } return response; } bool HttpClient::endOfBodyReached() { if (endOfHeadersReached() && (contentLength() != kNoContentLengthHeader)) { // We've got to the body and we know how long it will be return (iBodyLengthConsumed >= contentLength()); } return false; } int HttpClient::available() { if (iState == eReadingChunkLength) { while (iClient->available()) { char c = iClient->read(); if (c == '\n') { iState = eReadingBodyChunk; break; } else if (c == '\r') { // no-op } else if (isHexadecimalDigit(c)) { char digit[2] = {c, '\0'}; iChunkLength = (iChunkLength * 16) + strtol(digit, NULL, 16); } } } if (iState == eReadingBodyChunk && iChunkLength == 0) { iState = eReadingChunkLength; } if (iState == eReadingChunkLength) { return 0; } int clientAvailable = iClient->available(); if (iState == eReadingBodyChunk) { return min(clientAvailable, iChunkLength); } else { return clientAvailable; } } int HttpClient::read() { if (iIsChunked && !available()) { return -1; } int ret = iClient->read(); if (ret >= 0) { if (endOfHeadersReached() && iContentLength > 0) { // We're outputting the body now and we've seen a Content-Length header // So keep track of how many bytes are left iBodyLengthConsumed++; } if (iState == eReadingBodyChunk) { iChunkLength--; if (iChunkLength == 0) { iState = eReadingChunkLength; } } } return ret; } bool HttpClient::headerAvailable() { // clear the currently stored header line iHeaderLine = ""; while (!endOfHeadersReached()) { // read a byte from the header int c = readHeader(); if (c == '\r' || c == '\n') { if (iHeaderLine.length()) { // end of the line, all done break; } else { // ignore any CR or LF characters continue; } } // append byte to header line iHeaderLine += (char)c; } return (iHeaderLine.length() > 0); } String HttpClient::readHeaderName() { int colonIndex = iHeaderLine.indexOf(':'); if (colonIndex == -1) { return ""; } return iHeaderLine.substring(0, colonIndex); } String HttpClient::readHeaderValue() { int colonIndex = iHeaderLine.indexOf(':'); int startIndex = colonIndex + 1; if (colonIndex == -1) { return ""; } // trim any leading whitespace while (startIndex < (int)iHeaderLine.length() && isSpace(iHeaderLine[startIndex])) { startIndex++; } return iHeaderLine.substring(startIndex); } int HttpClient::read(uint8_t *buf, size_t size) { int ret =iClient->read(buf, size); if (endOfHeadersReached() && iContentLength > 0) { // We're outputting the body now and we've seen a Content-Length header // So keep track of how many bytes are left if (ret >= 0) { iBodyLengthConsumed += ret; } } return ret; } int HttpClient::readHeader() { char c = HttpClient::read(); if (endOfHeadersReached()) { // We've passed the headers, but rather than return an error, we'll just // act as a slightly less efficient version of read() return c; } // Whilst reading out the headers to whoever wants them, we'll keep an // eye out for the "Content-Length" header switch(iState) { case eStatusCodeRead: // We're at the start of a line, or somewhere in the middle of reading // the Content-Length prefix if (*iContentLengthPtr == c) { // This character matches, just move along iContentLengthPtr++; if (*iContentLengthPtr == '\0') { // We've reached the end of the prefix iState = eReadingContentLength; // Just in case we get multiple Content-Length headers, this // will ensure we just get the value of the last one iContentLength = 0; iBodyLengthConsumed = 0; } } else if (*iTransferEncodingChunkedPtr == c) { // This character matches, just move along iTransferEncodingChunkedPtr++; if (*iTransferEncodingChunkedPtr == '\0') { // We've reached the end of the Transfer Encoding: chunked header iIsChunked = true; iState = eSkipToEndOfHeader; } } else if (((iContentLengthPtr == kContentLengthPrefix) && (iTransferEncodingChunkedPtr == kTransferEncodingChunked)) && (c == '\r')) { // We've found a '\r' at the start of a line, so this is probably // the end of the headers iState = eLineStartingCRFound; } else { // This isn't the Content-Length or Transfer Encoding chunked header, skip to the end of the line iState = eSkipToEndOfHeader; } break; case eReadingContentLength: if (isdigit(c)) { long _iContentLength = iContentLength*10 + (c - '0'); // Only apply if the value didn't wrap around if (_iContentLength > iContentLength) { iContentLength = _iContentLength; } } else { // We've reached the end of the content length // We could sanity check it here or double-check for "\r\n" // rather than anything else, but let's be lenient iState = eSkipToEndOfHeader; } break; case eLineStartingCRFound: if (c == '\n') { if (iIsChunked) { iState = eReadingChunkLength; iChunkLength = 0; } else { iState = eReadingBody; } } break; default: // We're just waiting for the end of the line now break; }; if ( (c == '\n') && !endOfHeadersReached() ) { // We've got to the end of this line, start processing again iState = eStatusCodeRead; iContentLengthPtr = kContentLengthPrefix; iTransferEncodingChunkedPtr = kTransferEncodingChunked; } // And return the character read to whoever wants it return c; } 这是HttpClient.h头文件的内容// Class to simplify HTTP fetching on Arduino // (c) Copyright MCQN Ltd. 2010-2012 // Released under Apache License, version 2.0 #ifndef HttpClient_h #define HttpClient_h #include <Arduino.h> #include <IPAddress.h> #include "Client.h" static const int HTTP_SUCCESS =0; // The end of the headers has been reached. This consumes the '\n' // Could not connect to the server static const int HTTP_ERROR_CONNECTION_FAILED =-1; // This call was made when the HttpClient class wasn't expecting it // to be called. Usually indicates your code is using the class // incorrectly static const int HTTP_ERROR_API =-2; // Spent too long waiting for a reply static const int HTTP_ERROR_TIMED_OUT =-3; // The response from the server is invalid, is it definitely an HTTP // server? static const int HTTP_ERROR_INVALID_RESPONSE =-4; // Define some of the common methods and headers here // That lets other code reuse them without having to declare another copy // of them, so saves code space and RAM #define HTTP_METHOD_GET "GET" #define HTTP_METHOD_POST "POST" #define HTTP_METHOD_PUT "PUT" #define HTTP_METHOD_PATCH "PATCH" #define HTTP_METHOD_DELETE "DELETE" #define HTTP_HEADER_CONTENT_LENGTH "Content-Length" #define HTTP_HEADER_CONTENT_TYPE "Content-Type" #define HTTP_HEADER_CONNECTION "Connection" #define HTTP_HEADER_TRANSFER_ENCODING "Transfer-Encoding" #define HTTP_HEADER_USER_AGENT "User-Agent" #define HTTP_HEADER_VALUE_CHUNKED "chunked" class HttpClient : public Client { public: static const int kNoContentLengthHeader =-1; static const int kHttpPort =80; static const int kHttpsPort =443; static const char* kUserAgent; // FIXME Write longer API request, using port and user-agent, example // FIXME Update tempToPachube example to calculate Content-Length correctly HttpClient(Client& aClient, const char* aServerName, uint16_t aServerPort = kHttpPort); HttpClient(Client& aClient, const String& aServerName, uint16_t aServerPort = kHttpPort); HttpClient(Client& aClient, const IPAddress& aServerAddress, uint16_t aServerPort = kHttpPort); /** Start a more complex request. Use this when you need to send additional headers in the request, but you will also need to call endRequest() when you are finished. */ void beginRequest(); /** End a more complex request. Use this when you need to have sent additional headers in the request, but you will also need to call beginRequest() at the start. */ void endRequest(); /** Start the body of a more complex request. Use this when you need to send the body after additional headers in the request, but can optionally call endRequest() when you are finished. */ void beginBody(); /** Connect to the server and start to send a GET request. @param aURLPath Url to request @return 0 if successful, else error */ int get(const char* aURLPath); int get(const String& aURLPath); /** Connect to the server and start to send a POST request. @param aURLPath Url to request @return 0 if successful, else error */ int post(const char* aURLPath); int post(const String& aURLPath); /** Connect to the server and send a POST request with body and content type @param aURLPath Url to request @param aContentType Content type of request body @param aBody Body of the request @return 0 if successful, else error */ int post(const char* aURLPath, const char* aContentType, const char* aBody); int post(const String& aURLPath, const String& aContentType, const String& aBody); int post(const char* aURLPath, const char* aContentType, int aContentLength, const byte aBody[]); /** Connect to the server and start to send a PUT request. @param aURLPath Url to request @return 0 if successful, else error */ int put(const char* aURLPath); int put(const String& aURLPath); /** Connect to the server and send a PUT request with body and content type @param aURLPath Url to request @param aContentType Content type of request body @param aBody Body of the request @return 0 if successful, else error */ int put(const char* aURLPath, const char* aContentType, const char* aBody); int put(const String& aURLPath, const String& aContentType, const String& aBody); int put(const char* aURLPath, const char* aContentType, int aContentLength, const byte aBody[]); /** Connect to the server and start to send a PATCH request. @param aURLPath Url to request @return 0 if successful, else error */ int patch(const char* aURLPath); int patch(const String& aURLPath); /** Connect to the server and send a PATCH request with body and content type @param aURLPath Url to request @param aContentType Content type of request body @param aBody Body of the request @return 0 if successful, else error */ int patch(const char* aURLPath, const char* aContentType, const char* aBody); int patch(const String& aURLPath, const String& aContentType, const String& aBody); int patch(const char* aURLPath, const char* aContentType, int aContentLength, const byte aBody[]); /** Connect to the server and start to send a DELETE request. @param aURLPath Url to request @return 0 if successful, else error */ int del(const char* aURLPath); int del(const String& aURLPath); /** Connect to the server and send a DELETE request with body and content type @param aURLPath Url to request @param aContentType Content type of request body @param aBody Body of the request @return 0 if successful, else error */ int del(const char* aURLPath, const char* aContentType, const char* aBody); int del(const String& aURLPath, const String& aContentType, const String& aBody); int del(const char* aURLPath, const char* aContentType, int aContentLength, const byte aBody[]); /** Connect to the server and start to send the request. If a body is provided, the entire request (including headers and body) will be sent @param aURLPath Url to request @param aHttpMethod Type of HTTP request to make, e.g. "GET", "POST", etc. @param aContentType Content type of request body (optional) @param aContentLength Length of request body (optional) @param aBody Body of request (optional) @return 0 if successful, else error */ int startRequest(const char* aURLPath, const char* aHttpMethod, const char* aContentType = NULL, int aContentLength = -1, const byte aBody[] = NULL); /** Send an additional header line. This can only be called in between the calls to beginRequest and endRequest. @param aHeader Header line to send, in its entirety (but without the trailing CRLF. E.g. "Authorization: Basic YQDDCAIGES" */ void sendHeader(const char* aHeader); void sendHeader(const String& aHeader) { sendHeader(aHeader.c_str()); } /** Send an additional header line. This is an alternate form of sendHeader() which takes the header name and content as separate strings. The call will add the ": " to separate the header, so for example, to send a XXXXXX header call sendHeader("XXXXX", "Something") @param aHeaderName Type of header being sent @param aHeaderValue Value for that header */ void sendHeader(const char* aHeaderName, const char* aHeaderValue); void sendHeader(const String& aHeaderName, const String& aHeaderValue) { sendHeader(aHeaderName.c_str(), aHeaderValue.c_str()); } /** Send an additional header line. This is an alternate form of sendHeader() which takes the header name and content separately but where the value is provided as an integer. The call will add the ": " to separate the header, so for example, to send a XXXXXX header call sendHeader("XXXXX", 123) @param aHeaderName Type of header being sent @param aHeaderValue Value for that header */ void sendHeader(const char* aHeaderName, const int aHeaderValue); void sendHeader(const String& aHeaderName, const int aHeaderValue) { sendHeader(aHeaderName.c_str(), aHeaderValue); } /** Send a basic authentication header. This will encode the given username and password, and send them in suitable header line for doing Basic Authentication. @param aUser Username for the authorization @param aPassword Password for the user aUser */ void sendBasicAuth(const char* aUser, const char* aPassword); void sendBasicAuth(const String& aUser, const String& aPassword) { sendBasicAuth(aUser.c_str(), aPassword.c_str()); } /** Get the HTTP status code contained in the response. For example, 200 for successful request, 404 for file not found, etc. */ int responseStatusCode(); /** Check if a header is available to be read. Use readHeaderName() to read header name, and readHeaderValue() to read the header value MUST be called after responseStatusCode() and before contentLength() */ bool headerAvailable(); /** Read the name of the current response header. Returns empty string if a header is not available. */ String readHeaderName(); /** Read the value of the current response header. Returns empty string if a header is not available. */ String readHeaderValue(); /** Read the next character of the response headers. This functions in the same way as read() but to be used when reading through the headers. Check whether or not the end of the headers has been reached by calling endOfHeadersReached(), although after that point this will still return data as read() would, but slightly less efficiently MUST be called after responseStatusCode() and before contentLength() @return The next character of the response headers */ int readHeader(); /** Skip any response headers to get to the body. Use this if you don't want to do any special processing of the headers returned in the response. You can also use it after you've found all of the headers you're interested in, and just want to get on with processing the body. MUST be called after responseStatusCode() @return HTTP_SUCCESS if successful, else an error code */ int skipResponseHeaders(); /** Test whether all of the response headers have been consumed. @return true if we are now processing the response body, else false */ bool endOfHeadersReached(); /** Test whether the end of the body has been reached. Only works if the Content-Length header was returned by the server @return true if we are now at the end of the body, else false */ bool endOfBodyReached(); virtual bool endOfStream() { return endOfBodyReached(); }; virtual bool completed() { return endOfBodyReached(); }; /** Return the length of the body. Also skips response headers if they have not been read already MUST be called after responseStatusCode() @return Length of the body, in bytes, or kNoContentLengthHeader if no Content-Length header was returned by the server */ long contentLength(); /** Returns if the response body is chunked @return true if response body is chunked, false otherwise */ int isResponseChunked() { return iIsChunked; } /** Return the response body as a String Also skips response headers if they have not been read already MUST be called after responseStatusCode() @return response body of request as a String */ String responseBody(); /** Enables connection keep-alive mode */ void connectionKeepAlive(); /** Disables sending the default request headers (Host and User Agent) */ void noDefaultRequestHeaders(); // Inherited from Print // Note: 1st call to these indicates the user is sending the body, so if need // Note: be we should finish the header first virtual size_t write(uint8_t aByte) { if (iState < eRequestSent) { finishHeaders(); }; return iClient-> write(aByte); }; virtual size_t write(const uint8_t *aBuffer, size_t aSize) { if (iState < eRequestSent) { finishHeaders(); }; return iClient->write(aBuffer, aSize); }; // Inherited from Stream virtual int available(); /** Read the next byte from the server. @return Byte read or -1 if there are no bytes available. */ virtual int read(); virtual int read(uint8_t *buf, size_t size); virtual int peek() { return iClient->peek(); }; virtual void flush() { iClient->flush(); }; // Inherited from Client virtual int connect(IPAddress ip, uint16_t port) { return iClient->connect(ip, port); }; virtual int connect(const char *host, uint16_t port) { return iClient->connect(host, port); }; virtual void stop(); virtual uint8_t connected() { return iClient->connected(); }; virtual operator bool() { return bool(iClient); }; virtual uint32_t httpResponseTimeout() { return iHttpResponseTimeout; }; virtual void setHttpResponseTimeout(uint32_t timeout) { iHttpResponseTimeout = timeout; }; virtual uint32_t httpWaitForDataDelay() { return iHttpWaitForDataDelay; }; virtual void setHttpWaitForDataDelay(uint32_t delay) { iHttpWaitForDataDelay = delay; }; protected: /** Reset internal state data back to the "just initialised" state */ void resetState(); /** Send the first part of the request and the initial headers. @param aURLPath Url to request @param aHttpMethod Type of HTTP request to make, e.g. "GET", "POST", etc. @return 0 if successful, else error */ int sendInitialHeaders(const char* aURLPath, const char* aHttpMethod); /* Let the server know that we've reached the end of the headers */ void finishHeaders(); /** Reading any pending data from the client (used in connection keep alive mode) */ void flushClientRx(); // Number of milliseconds that we wait each time there isn't any data // available to be read (during status code and header processing) static const int kHttpWaitForDataDelay = 100; // Number of milliseconds that we'll wait in total without receiving any // data before returning HTTP_ERROR_TIMED_OUT (during status code and header // processing) static const int kHttpResponseTimeout = 30*1000; static const char* kContentLengthPrefix; static const char* kTransferEncodingChunked; typedef enum { eIdle, eRequestStarted, eRequestSent, eReadingStatusCode, eStatusCodeRead, eReadingContentLength, eSkipToEndOfHeader, eLineStartingCRFound, eReadingBody, eReadingChunkLength, eReadingBodyChunk } tHttpState; // Client we're using Client* iClient; // Server we are connecting to const char* iServerName; IPAddress iServerAddress; // Port of server we are connecting to uint16_t iServerPort; // Current state of the finite-state-machine tHttpState iState; // Stores the status code for the response, once known int iStatusCode; // Stores the value of the Content-Length header, if present long iContentLength; // How many bytes of the response body have been read by the user int iBodyLengthConsumed; // How far through a Content-Length header prefix we are const char* iContentLengthPtr; // How far through a Transfer-Encoding chunked header we are const char* iTransferEncodingChunkedPtr; // Stores if the response body is chunked bool iIsChunked; // Stores the value of the current chunk length, if present int iChunkLength; uint32_t iHttpResponseTimeout; uint32_t iHttpWaitForDataDelay; bool iConnectionClose; bool iSendDefaultRequestHeaders; String iHeaderLine; }; #endif 能否基于https://docs.arduino.cc/libraries/ethernet/#Client%20Class中的EthernetClient帮我仿照实现下所有对应的功能
最新发布
09-03
/* -*- C -*- * main.c -- the bare scullp char module * * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet * Copyright (C) 2001 O'Reilly & Associates * * The source code in this file can be freely used, adapted, * and redistributed in source or binary form, so long as an * acknowledgment appears in derived source files. The citation * should list that the code comes from the book "Linux Device * Drivers" by Alessandro Rubini and Jonathan Corbet, published * by O'Reilly & Associates. No warranty is attached; * we cannot take responsibility for errors or fitness for use. * * $Id: _main.c.in,v 1.21 2004/10/14 20:11:39 corbet Exp $ */ #include <linux/config.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/kernel.h> /* printk() */ #include <linux/slab.h> /* kmalloc() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/proc_fs.h> #include <linux/fcntl.h> /* O_ACCMODE */ #include <linux/aio.h> #include <asm/uaccess.h> #include "scullp.h" /* local definitions */ int scullp_major = SCULLP_MAJOR; int scullp_devs = SCULLP_DEVS; /* number of bare scullp devices */ int scullp_qset = SCULLP_QSET; int scullp_order = SCULLP_ORDER; module_param(scullp_major, int, 0); module_param(scullp_devs, int, 0); module_param(scullp_qset, int, 0); module_param(scullp_order, int, 0); MODULE_AUTHOR("Alessandro Rubini"); MODULE_LICENSE("Dual BSD/GPL"); struct scullp_dev *scullp_devices; /* allocated in scullp_init */ int scullp_trim(struct scullp_dev *dev); void scullp_cleanup(void); #ifdef SCULLP_USE_PROC /* don't waste space if unused */ /* * The proc filesystem: function to read and entry */ void scullp_proc_offset(char *buf, char **start, off_t *offset, int *len) { if (*offset == 0) return; if (*offset >= *len) { /* Not there yet */ *offset -= *len; *len = 0; } else { /* We're into the interesting stuff now */ *start = buf + *offset; *offset = 0; } } /* FIXME: Do we need this here?? It be ugly */ int scullp_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data) { int i, j, order, qset, len = 0; int limit = count - 80; /* Don't print more than this */ struct scullp_dev *d; *start = buf; for(i = 0; i < scullp_devs; i++) { d = &scullp_devices[i]; if (down_interruptible (&d->sem)) return -ERESTARTSYS; qset = d->qset; /* retrieve the features of each device */ order = d->order; len += sprintf(buf+len,"\nDevice %i: qset %i, order %i, sz %li\n", i, qset, order, (long)(d->size)); for (; d; d = d->next) { /* scan the list */ len += sprintf(buf+len," item at %p, qset at %p\n",d,d->data); scullp_proc_offset (buf, start, &offset, &len); if (len > limit) goto out; if (d->data && !d->next) /* dump only the last item - save space */ for (j = 0; j < qset; j++) { if (d->data[j]) len += sprintf(buf+len," % 4i:%8p\n",j,d->data[j]); scullp_proc_offset (buf, start, &offset, &len); if (len > limit) goto out; } } out: up (&scullp_devices[i].sem); if (len > limit) break; } *eof = 1; return len; } #endif /* SCULLP_USE_PROC */ /* * Open and close */ int scullp_open (struct inode *inode, struct file *filp) { struct scullp_dev *dev; /* device information */ /* Find the device */ dev = container_of(inode->i_cdev, struct scullp_dev, cdev); /* now trim to 0 the length of the device if open was write-only */ if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) { if (down_interruptible (&dev->sem)) return -ERESTARTSYS; scullp_trim(dev); /* ignore errors */ up (&dev->sem); } /* and use filp->private_data to point to the device data */ filp->private_data = dev; return 0; /* success */ } int scullp_release (struct inode *inode, struct file *filp) { return 0; } /* * Follow the list */ struct scullp_dev *scullp_follow(struct scullp_dev *dev, int n) { while (n--) { if (!dev->next) { dev->next = kmalloc(sizeof(struct scullp_dev), GFP_KERNEL); memset(dev->next, 0, sizeof(struct scullp_dev)); } dev = dev->next; continue; } return dev; } /* * Data management: read and write */ ssize_t scullp_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct scullp_dev *dev = filp->private_data; /* the first listitem */ struct scullp_dev *dptr; int quantum = PAGE_SIZE << dev->order; int qset = dev->qset; int itemsize = quantum * qset; /* how many bytes in the listitem */ int item, s_pos, q_pos, rest; ssize_t retval = 0; if (down_interruptible (&dev->sem)) return -ERESTARTSYS; if (*f_pos > dev->size) goto nothing; if (*f_pos + count > dev->size) count = dev->size - *f_pos; /* find listitem, qset index, and offset in the quantum */ item = ((long) *f_pos) / itemsize; rest = ((long) *f_pos) % itemsize; s_pos = rest / quantum; q_pos = rest % quantum; /* follow the list up to the right position (defined elsewhere) */ dptr = scullp_follow(dev, item); if (!dptr->data) goto nothing; /* don't fill holes */ if (!dptr->data[s_pos]) goto nothing; if (count > quantum - q_pos) count = quantum - q_pos; /* read only up to the end of this quantum */ if (copy_to_user (buf, dptr->data[s_pos]+q_pos, count)) { retval = -EFAULT; goto nothing; } up (&dev->sem); *f_pos += count; return count; nothing: up (&dev->sem); return retval; } ssize_t scullp_write (struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct scullp_dev *dev = filp->private_data; struct scullp_dev *dptr; int quantum = PAGE_SIZE << dev->order; int qset = dev->qset; int itemsize = quantum * qset; int item, s_pos, q_pos, rest; ssize_t retval = -ENOMEM; /* our most likely error */ if (down_interruptible (&dev->sem)) return -ERESTARTSYS; /* find listitem, qset index and offset in the quantum */ item = ((long) *f_pos) / itemsize; rest = ((long) *f_pos) % itemsize; s_pos = rest / quantum; q_pos = rest % quantum; /* follow the list up to the right position */ dptr = scullp_follow(dev, item); if (!dptr->data) { dptr->data = kmalloc(qset * sizeof(void *), GFP_KERNEL); if (!dptr->data) goto nomem; memset(dptr->data, 0, qset * sizeof(char *)); } /* Here's the allocation of a single quantum */ if (!dptr->data[s_pos]) { dptr->data[s_pos] = (void *)__get_free_pages(GFP_KERNEL, dptr->order); if (!dptr->data[s_pos]) goto nomem; memset(dptr->data[s_pos], 0, PAGE_SIZE << dptr->order); } if (count > quantum - q_pos) count = quantum - q_pos; /* write only up to the end of this quantum */ if (copy_from_user (dptr->data[s_pos]+q_pos, buf, count)) { retval = -EFAULT; goto nomem; } *f_pos += count; /* update the size */ if (dev->size < *f_pos) dev->size = *f_pos; up (&dev->sem); return count; nomem: up (&dev->sem); return retval; } /* * The ioctl() implementation */ int scullp_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int err = 0, ret = 0, tmp; /* don't even decode wrong cmds: better returning ENOTTY than EFAULT */ if (_IOC_TYPE(cmd) != SCULLP_IOC_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > SCULLP_IOC_MAXNR) return -ENOTTY; /* * the type is a bitmask, and VERIFY_WRITE catches R/W * transfers. Note that the type is user-oriented, while * verify_area is kernel-oriented, so the concept of "read" and * "write" is reversed */ if (_IOC_DIR(cmd) & _IOC_READ) err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); else if (_IOC_DIR(cmd) & _IOC_WRITE) err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); if (err) return -EFAULT; switch(cmd) { case SCULLP_IOCRESET: scullp_qset = SCULLP_QSET; scullp_order = SCULLP_ORDER; break; case SCULLP_IOCSORDER: /* Set: arg points to the value */ ret = __get_user(scullp_order, (int __user *) arg); break; case SCULLP_IOCTORDER: /* Tell: arg is the value */ scullp_order = arg; break; case SCULLP_IOCGORDER: /* Get: arg is pointer to result */ ret = __put_user (scullp_order, (int __user *) arg); break; case SCULLP_IOCQORDER: /* Query: return it (it's positive) */ return scullp_order; case SCULLP_IOCXORDER: /* eXchange: use arg as pointer */ tmp = scullp_order; ret = __get_user(scullp_order, (int __user *) arg); if (ret == 0) ret = __put_user(tmp, (int __user *) arg); break; case SCULLP_IOCHORDER: /* sHift: like Tell + Query */ tmp = scullp_order; scullp_order = arg; return tmp; case SCULLP_IOCSQSET: ret = __get_user(scullp_qset, (int __user *) arg); break; case SCULLP_IOCTQSET: scullp_qset = arg; break; case SCULLP_IOCGQSET: ret = __put_user(scullp_qset, (int __user *)arg); break; case SCULLP_IOCQQSET: return scullp_qset; case SCULLP_IOCXQSET: tmp = scullp_qset; ret = __get_user(scullp_qset, (int __user *)arg); if (ret == 0) ret = __put_user(tmp, (int __user *)arg); break; case SCULLP_IOCHQSET: tmp = scullp_qset; scullp_qset = arg; return tmp; default: /* redundant, as cmd was checked against MAXNR */ return -ENOTTY; } return ret; } /* * The "extended" operations */ loff_t scullp_llseek (struct file *filp, loff_t off, int whence) { struct scullp_dev *dev = filp->private_data; long newpos; switch(whence) { case 0: /* SEEK_SET */ newpos = off; break; case 1: /* SEEK_CUR */ newpos = filp->f_pos + off; break; case 2: /* SEEK_END */ newpos = dev->size + off; break; default: /* can't happen */ return -EINVAL; } if (newpos<0) return -EINVAL; filp->f_pos = newpos; return newpos; } /* * A simple asynchronous I/O implementation. */ struct async_work { struct kiocb *iocb; int result; struct work_struct work; }; /* * "Complete" an asynchronous operation. */ static void scullp_do_deferred_op(void *p) { struct async_work *stuff = (struct async_work *) p; aio_complete(stuff->iocb, stuff->result, 0); kfree(stuff); } static int scullp_defer_op(int write, struct kiocb *iocb, char __user *buf, size_t count, loff_t pos) { struct async_work *stuff; int result; /* Copy now while we can access the buffer */ if (write) result = scullp_write(iocb->ki_filp, buf, count, &pos); else result = scullp_read(iocb->ki_filp, buf, count, &pos); /* If this is a synchronous IOCB, we return our status now. */ if (is_sync_kiocb(iocb)) return result; /* Otherwise defer the completion for a few milliseconds. */ stuff = kmalloc (sizeof (*stuff), GFP_KERNEL); if (stuff == NULL) return result; /* No memory, just complete now */ stuff->iocb = iocb; stuff->result = result; INIT_WORK(&stuff->work, scullp_do_deferred_op, stuff); schedule_delayed_work(&stuff->work, HZ/100); return -EIOCBQUEUED; } static ssize_t scullp_aio_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t pos) { return scullp_defer_op(0, iocb, buf, count, pos); } static ssize_t scullp_aio_write(struct kiocb *iocb, const char __user *buf, size_t count, loff_t pos) { return scullp_defer_op(1, iocb, (char __user *) buf, count, pos); } /* * Mmap *is* available, but confined in a different file */ extern int scullp_mmap(struct file *filp, struct vm_area_struct *vma); /* * The fops */ struct file_operations scullp_fops = { .owner = THIS_MODULE, .llseek = scullp_llseek, .read = scullp_read, .write = scullp_write, .ioctl = scullp_ioctl, .mmap = scullp_mmap, .open = scullp_open, .release = scullp_release, .aio_read = scullp_aio_read, .aio_write = scullp_aio_write, }; int scullp_trim(struct scullp_dev *dev) { struct scullp_dev *next, *dptr; int qset = dev->qset; /* "dev" is not-null */ int i; if (dev->vmas) /* don't trim: there are active mappings */ return -EBUSY; for (dptr = dev; dptr; dptr = next) { /* all the list items */ if (dptr->data) { /* This code frees a whole quantum-set */ for (i = 0; i < qset; i++) if (dptr->data[i]) free_pages((unsigned long)(dptr->data[i]), dptr->order); kfree(dptr->data); dptr->data=NULL; } next=dptr->next; if (dptr != dev) kfree(dptr); /* all of them but the first */ } dev->size = 0; dev->qset = scullp_qset; dev->order = scullp_order; dev->next = NULL; return 0; } static void scullp_setup_cdev(struct scullp_dev *dev, int index) { int err, devno = MKDEV(scullp_major, index); cdev_init(&dev->cdev, &scullp_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &scullp_fops; err = cdev_add (&dev->cdev, devno, 1); /* Fail gracefully if need be */ if (err) printk(KERN_NOTICE "Error %d adding scull%d", err, index); } /* * Finally, the module stuff */ int scullp_init(void) { int result, i; dev_t dev = MKDEV(scullp_major, 0); /* * Register your major, and accept a dynamic number. */ if (scullp_major) result = register_chrdev_region(dev, scullp_devs, "scullp"); else { result = alloc_chrdev_region(&dev, 0, scullp_devs, "scullp"); scullp_major = MAJOR(dev); } if (result < 0) return result; /* * allocate the devices -- we can't have them static, as the number * can be specified at load time */ scullp_devices = kmalloc(scullp_devs*sizeof (struct scullp_dev), GFP_KERNEL); if (!scullp_devices) { result = -ENOMEM; goto fail_malloc; } memset(scullp_devices, 0, scullp_devs*sizeof (struct scullp_dev)); for (i = 0; i < scullp_devs; i++) { scullp_devices[i].order = scullp_order; scullp_devices[i].qset = scullp_qset; sema_init (&scullp_devices[i].sem, 1); scullp_setup_cdev(scullp_devices + i, i); } #ifdef SCULLP_USE_PROC /* only when available */ create_proc_read_entry("scullpmem", 0, NULL, scullp_read_procmem, NULL); #endif return 0; /* succeed */ fail_malloc: unregister_chrdev_region(dev, scullp_devs); return result; } void scullp_cleanup(void) { int i; #ifdef SCULLP_USE_PROC remove_proc_entry("scullpmem", NULL); #endif for (i = 0; i < scullp_devs; i++) { cdev_del(&scullp_devices[i].cdev); scullp_trim(scullp_devices + i); } kfree(scullp_devices); unregister_chrdev_region(MKDEV (scullp_major, 0), scullp_devs); } module_init(scullp_init); module_exit(scullp_cleanup);详细说一下这个文件里的异步IO机制
06-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值